Backed out 4 changesets (bug 1486954) for hangs on Linux. a=backout

Backed out changeset c895888bdddc (bug 1486954)
Backed out changeset 27e9286503e8 (bug 1486954)
Backed out changeset 87e64652386d (bug 1486954)
Backed out changeset 96a6e1ceb697 (bug 1486954)

--HG--
rename : browser/extensions/formautofill/OSKeyStore.jsm => browser/extensions/formautofill/MasterPassword.jsm
rename : browser/extensions/formautofill/test/browser/browser_creditCard_fill_cancel_login.js => browser/extensions/formautofill/test/browser/browser_creditCard_fill_master_password.js
This commit is contained in:
Margareta Eliza Balazs 2018-10-18 12:40:21 +03:00
Родитель 2b838cf300
Коммит 53fc8ddacd
34 изменённых файлов: 696 добавлений и 1061 удалений

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

@ -16,8 +16,8 @@ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "OSKeyStore",
"resource://formautofill/OSKeyStore.jsm");
ChromeUtils.defineModuleGetter(this, "MasterPassword",
"resource://formautofill/MasterPassword.jsm");
ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
@ -149,7 +149,7 @@ var paymentDialogWrapper = {
/**
* @param {string} guid The GUID of the basic card record from storage.
* @param {string} cardSecurityCode The associated card security code (CVV/CCV/etc.)
* @throws If there is an error decrypting
* @throws if the user cancels entering their master password or an error decrypting
* @returns {nsIBasicCardResponseData?} returns response data or null (if the
* master password dialog was cancelled);
*/
@ -162,7 +162,7 @@ var paymentDialogWrapper = {
let cardNumber;
try {
cardNumber = await OSKeyStore.decrypt(cardData["cc-number-encrypted"], true);
cardNumber = await MasterPassword.decrypt(cardData["cc-number-encrypted"], true);
} catch (ex) {
if (ex.result != Cr.NS_ERROR_ABORT) {
throw ex;
@ -503,16 +503,8 @@ var paymentDialogWrapper = {
selectedPaymentCardGUID: paymentCardGUID,
selectedPaymentCardSecurityCode: cardSecurityCode,
}) {
let methodData;
try {
methodData = await this._convertProfileBasicCardToPaymentMethodData(paymentCardGUID,
cardSecurityCode);
} catch (ex) {
// TODO (Bug 1498403): Some kind of "credit card storage error" here, perhaps asking user
// to re-enter credit card # from management UI.
Cu.reportError(ex);
return;
}
let methodData = await this._convertProfileBasicCardToPaymentMethodData(paymentCardGUID,
cardSecurityCode);
if (!methodData) {
// TODO (Bug 1429265/Bug 1429205): Handle when a user hits cancel on the

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

@ -318,7 +318,7 @@ let BASIC_CARDS_1 = {
methodName: "basic-card",
"cc-number": "************5461",
"guid": "53f9d009aed2",
"version": 2,
"version": 1,
"timeCreated": 1505240896213,
"timeLastModified": 1515609524588,
"timeLastUsed": 0,
@ -336,7 +336,7 @@ let BASIC_CARDS_1 = {
methodName: "basic-card",
"cc-number": "************0954",
"guid": "9h5d4h6f4d1s",
"version": 2,
"version": 1,
"timeCreated": 1517890536491,
"timeLastModified": 1517890564518,
"timeLastUsed": 0,
@ -354,7 +354,7 @@ let BASIC_CARDS_1 = {
methodName: "basic-card",
"cc-number": "************1234",
"guid": "123456789abc",
"version": 2,
"version": 1,
"timeCreated": 1517890536491,
"timeLastModified": 1517890564518,
"timeLastUsed": 0,
@ -388,7 +388,7 @@ let BASIC_CARDS_1 = {
methodName: "basic-card",
"cc-number": "************8563",
"guid": "missing-cc-name",
"version": 2,
"version": 1,
"timeCreated": 1517890536491,
"timeLastModified": 1517890564518,
"timeLastUsed": 0,

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

@ -42,7 +42,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
FormAutofillPreferences: "resource://formautofill/FormAutofillPreferences.jsm",
FormAutofillDoorhanger: "resource://formautofill/FormAutofillDoorhanger.jsm",
FormAutofillUtils: "resource://formautofill/FormAutofillUtils.jsm",
OSKeyStore: "resource://formautofill/OSKeyStore.jsm",
MasterPassword: "resource://formautofill/MasterPassword.jsm",
});
this.log = null;
@ -226,8 +226,8 @@ FormAutofillParent.prototype = {
break;
}
case "FormAutofill:SaveCreditCard": {
if (!await OSKeyStore.ensureLoggedIn()) {
log.warn("User canceled encryption login");
if (!await MasterPassword.ensureLoggedIn()) {
log.warn("User canceled master password entry");
return;
}
await this.formAutofillStorage.creditCards.add(data.creditcard);
@ -254,12 +254,12 @@ FormAutofillParent.prototype = {
let {cipherText, reauth} = data;
let string;
try {
string = await OSKeyStore.decrypt(cipherText, reauth);
string = await MasterPassword.decrypt(cipherText, reauth);
} catch (e) {
if (e.result != Cr.NS_ERROR_ABORT) {
throw e;
}
log.warn("User canceled encryption login");
log.warn("User canceled master password entry");
}
target.sendAsyncMessage("FormAutofill:DecryptedString", string);
break;
@ -293,7 +293,7 @@ FormAutofillParent.prototype = {
/**
* Get the records from profile store and return results back to content
* process. It will decrypt the credit card number and append
* "cc-number-decrypted" to each record if OSKeyStore isn't set.
* "cc-number-decrypted" to each record if MasterPassword isn't set.
*
* @private
* @param {string} data.collectionName
@ -318,9 +318,9 @@ FormAutofillParent.prototype = {
return;
}
let isCC = collectionName == CREDITCARDS_COLLECTION_NAME;
// We don't filter "cc-number"
if (isCC && info.fieldName == "cc-number") {
let isCCAndMPEnabled = collectionName == CREDITCARDS_COLLECTION_NAME && MasterPassword.isEnabled;
// We don't filter "cc-number" when MasterPassword is set.
if (isCCAndMPEnabled && info.fieldName == "cc-number") {
recordsInCollection = recordsInCollection.filter(record => !!record["cc-number"]);
target.sendAsyncMessage("FormAutofill:Records", recordsInCollection);
return;
@ -335,6 +335,17 @@ FormAutofillParent.prototype = {
continue;
}
// Cache the decrypted "cc-number" in each record for content to preview
// when MasterPassword isn't set.
if (!isCCAndMPEnabled && record["cc-number-encrypted"]) {
record["cc-number-decrypted"] = await MasterPassword.decrypt(record["cc-number-encrypted"]);
}
// Filter "cc-number" based on the decrypted one.
if (info.fieldName == "cc-number") {
fieldValue = record["cc-number-decrypted"];
}
if (collectionName == ADDRESSES_COLLECTION_NAME && record.country
&& !FormAutofill.supportedCountries.includes(record.country)) {
// Address autofill isn't supported for the record's country so we don't
@ -528,8 +539,8 @@ FormAutofillParent.prototype = {
return;
}
if (!await OSKeyStore.ensureLoggedIn()) {
log.warn("User canceled encryption login");
if (!await MasterPassword.ensureLoggedIn()) {
log.warn("User canceled master password entry");
return;
}

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

@ -142,14 +142,11 @@ ChromeUtils.defineModuleGetter(this, "FormAutofillNameUtils",
"resource://formautofill/FormAutofillNameUtils.jsm");
ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
"resource://formautofill/FormAutofillUtils.jsm");
ChromeUtils.defineModuleGetter(this, "OSKeyStore",
"resource://formautofill/OSKeyStore.jsm");
ChromeUtils.defineModuleGetter(this, "MasterPassword",
"resource://formautofill/MasterPassword.jsm");
ChromeUtils.defineModuleGetter(this, "PhoneNumber",
"resource://formautofill/phonenumberutils/PhoneNumber.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cryptoSDR",
"@mozilla.org/login-manager/crypto/SDR;1",
Ci.nsILoginManagerCrypto);
XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
@ -161,7 +158,7 @@ const PROFILE_JSON_FILE_NAME = "autofill-profiles.json";
const STORAGE_SCHEMA_VERSION = 1;
const ADDRESS_SCHEMA_VERSION = 1;
const CREDIT_CARD_SCHEMA_VERSION = 2;
const CREDIT_CARD_SCHEMA_VERSION = 1;
const VALID_ADDRESS_FIELDS = [
"given-name",
@ -267,14 +264,13 @@ class AutofillRecords {
this._collectionName = collectionName;
this._schemaVersion = schemaVersion;
this._initializePromise =
Promise.all(this._data.map(async (record, index) => this._migrateRecord(record, index)))
.then(hasChangesArr => {
let dataHasChanges = hasChangesArr.includes(true);
if (dataHasChanges) {
this._store.saveSoon();
}
});
Promise.all(this._data.map(record => this._migrateRecord(record)))
.then(hasChangesArr => {
let dataHasChanges = hasChangesArr.find(hasChanges => hasChanges);
if (dataHasChanges) {
this._store.saveSoon();
}
});
}
/**
@ -307,14 +303,6 @@ class AutofillRecords {
}
}
/**
* Initialize the records in the collection, resolves when the migration completes.
* @returns {Promise}
*/
initialize() {
return this._initializePromise;
}
/**
* Adds a new record.
*
@ -1188,7 +1176,7 @@ class AutofillRecords {
});
}
async _migrateRecord(record, index) {
async _migrateRecord(record) {
let hasChanges = false;
if (record.deleted) {
@ -1204,21 +1192,10 @@ class AutofillRecords {
if (record.version < this.version) {
hasChanges = true;
record.version = this.version;
record = await this._computeMigratedRecord(record);
if (record.deleted) {
// record is deleted by _computeMigratedRecord(),
// go ahead and put it in the store.
this._data[index] = record;
return hasChanges;
}
// Compute the computed fields before putting it to store.
await this.computeFields(record);
this._data[index] = record;
return hasChanges;
// Force to recompute fields if we upgrade the schema.
await this._stripComputedFields(record);
}
hasChanges |= await this.computeFields(record);
@ -1279,24 +1256,6 @@ class AutofillRecords {
}}, "formautofill-storage-changed", "removeAll");
}
/**
* Strip the computed fields based on the record version.
* @param {Object} record The record to migrate
* @returns {Object} Migrated record.
* Record is always cloned, with version updated,
* with computed fields stripped.
* Could be a tombstone record, if the record
* should be discorded.
*/
async _computeMigratedRecord(record) {
if (!record.deleted) {
record = this._clone(record);
await this._stripComputedFields(record);
record.version = this.version;
}
return record;
}
async _stripComputedFields(record) {
this.VALID_COMPUTED_FIELDS.forEach(field => delete record[field]);
}
@ -1645,7 +1604,7 @@ class CreditCards extends AutofillRecords {
if ("cc-number" in creditCard) {
let ccNumber = creditCard["cc-number"];
creditCard["cc-number"] = CreditCard.getLongMaskedNumber(ccNumber);
creditCard["cc-number-encrypted"] = await OSKeyStore.encrypt(ccNumber);
creditCard["cc-number-encrypted"] = await MasterPassword.encrypt(ccNumber);
} else {
creditCard["cc-number-encrypted"] = "";
}
@ -1654,63 +1613,9 @@ class CreditCards extends AutofillRecords {
return hasNewComputedFields;
}
async _computeMigratedRecord(creditCard) {
if (creditCard["cc-number-encrypted"]) {
switch (creditCard.version) {
case 1: {
if (!cryptoSDR.isLoggedIn) {
// We cannot decrypt the data, so silently remove the record for
// the user.
if (creditCard.deleted) {
break;
}
this.log.warn("Removing version 1 credit card record to migrate to new encryption:", creditCard.guid);
// Replace the record with a tombstone record here,
// regardless of existence of sync metadata.
let existingSync = this._getSyncMetaData(creditCard);
creditCard = {
guid: creditCard.guid,
timeLastModified: Date.now(),
deleted: true,
};
if (existingSync) {
creditCard._sync = existingSync;
existingSync.changeCounter++;
}
break;
}
creditCard = this._clone(creditCard);
// Decrypt the cc-number using version 1 encryption.
let ccNumber = cryptoSDR.decrypt(creditCard["cc-number-encrypted"]);
// Re-encrypt the cc-number with version 2 encryption.
creditCard["cc-number-encrypted"] = await OSKeyStore.encrypt(ccNumber);
break;
}
default:
throw new Error("Unknown credit card version to migrate: " + creditCard.version);
}
}
return super._computeMigratedRecord(creditCard);
}
async _stripComputedFields(creditCard) {
if (creditCard["cc-number-encrypted"]) {
try {
creditCard["cc-number"] = await OSKeyStore.decrypt(creditCard["cc-number-encrypted"]);
} catch (ex) {
if (ex.result == Cr.NS_ERROR_ABORT) {
throw ex;
}
// Quietly recover from encryption error,
// so existing credit card entry with undecryptable number
// can be updated.
}
creditCard["cc-number"] = await MasterPassword.decrypt(creditCard["cc-number-encrypted"]);
}
await super._stripComputedFields(creditCard);
}
@ -1771,33 +1676,6 @@ class CreditCards extends AutofillRecords {
}
}
_ensureMatchingVersion(record) {
if (!record.version || isNaN(record.version) || record.version < 1) {
throw new Error(`Got invalid record version ${
record.version}; want ${this.version}`);
}
if (record.version < this.version) {
switch (record.version) {
case 1:
// The difference between version 1 and 2 is only about the encryption
// method used for the cc-number-encrypted field.
// As long as the record is already decrypted, it is safe to bump the
// version directly.
if (!record["cc-number-encrypted"]) {
record.version = this.version;
} else {
throw new Error("Unexpected record migration path.");
}
break;
default:
throw new Error("Unknown credit card version to match: " + record.version);
}
}
return super._ensureMatchingVersion(record);
}
/**
* Normalize the given record and return the first matched guid if storage has the same record.
* @param {Object} targetCreditCard
@ -1814,9 +1692,12 @@ class CreditCards extends AutofillRecords {
return !creditCard[field];
}
if (field == "cc-number" && creditCard[field]) {
// Compare the masked numbers instead when decryption requires a password
// because we don't want to leak the credit card number.
return CreditCard.getLongMaskedNumber(clonedTargetCreditCard[field]) == creditCard[field];
if (MasterPassword.isEnabled) {
// Compare the masked numbers instead when the master password is
// enabled because we don't want to leak the credit card number.
return CreditCard.getLongMaskedNumber(clonedTargetCreditCard[field]) == creditCard[field];
}
return (clonedTargetCreditCard[field] == await MasterPassword.decrypt(creditCard["cc-number-encrypted"]));
}
return clonedTargetCreditCard[field] == creditCard[field];
})).then(fieldResults => fieldResults.every(result => result));
@ -1924,10 +1805,7 @@ FormAutofillStorage.prototype = {
path: this._path,
dataPostProcessor: this._dataPostProcessor.bind(this),
});
this._initializePromise = this._store.load()
.then(() => Promise.all([
this.addresses.initialize(),
this.creditCards.initialize()]));
this._initializePromise = this._store.load();
}
return this._initializePromise;
},

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

@ -17,7 +17,7 @@ const EDIT_ADDRESS_KEYWORDS = [
"givenName", "additionalName", "familyName", "organization2", "streetAddress",
"state", "province", "city", "country", "zip", "postalCode", "email", "tel",
];
const MANAGE_CREDITCARDS_KEYWORDS = ["manageCreditCardsTitle", "addNewCreditCardTitle"];
const MANAGE_CREDITCARDS_KEYWORDS = ["manageCreditCardsTitle", "addNewCreditCardTitle", "showCreditCardsBtnLabel"];
const EDIT_CREDITCARD_KEYWORDS = ["cardNumber", "nameOnCard", "cardExpiresMonth", "cardExpiresYear", "cardNetwork"];
const FIELD_STATES = {
NORMAL: "NORMAL",

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

@ -0,0 +1,184 @@
/* 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/. */
/**
* Helpers for the Master Password Dialog.
* In the future the Master Password implementation may move here.
*/
"use strict";
var EXPORTED_SYMBOLS = [
"MasterPassword",
];
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cryptoSDR",
"@mozilla.org/login-manager/crypto/SDR;1",
Ci.nsILoginManagerCrypto);
var MasterPassword = {
get _token() {
let tokendb = Cc["@mozilla.org/security/pk11tokendb;1"].createInstance(Ci.nsIPK11TokenDB);
return tokendb.getInternalKeyToken();
},
/**
* @returns {boolean} True if a master password is set and false otherwise.
*/
get isEnabled() {
return this._token.hasPassword;
},
/**
* @returns {boolean} True if master password is logged in and false if not.
*/
get isLoggedIn() {
return Services.logins.isLoggedIn;
},
/**
* @returns {boolean} True if there is another master password login dialog
* existing and false otherwise.
*/
get isUIBusy() {
return Services.logins.uiBusy;
},
/**
* Ensure the master password is logged in. It will display the master password
* login prompt or do nothing if it's logged in already. If an existing MP
* prompt is already prompted, the result from it will be used instead.
*
* @param {boolean} reauth Prompt the login dialog no matter it's logged in
* or not if it's set to true.
* @returns {Promise<boolean>} True if it's logged in or no password is set
* and false if it's still not logged in (prompt
* canceled or other error).
*/
async ensureLoggedIn(reauth = false) {
if (!this.isEnabled) {
return true;
}
if (this.isLoggedIn && !reauth) {
return true;
}
// If a prompt is already showing then wait for and focus it.
if (this.isUIBusy) {
return this.waitForExistingDialog();
}
let token = this._token;
try {
// 'true' means always prompt for token password. User will be prompted until
// clicking 'Cancel' or entering the correct password.
token.login(true);
} catch (e) {
// An exception will be thrown if the user cancels the login prompt dialog.
// User is also logged out.
}
// If we triggered a master password prompt, notify observers.
if (token.isLoggedIn()) {
Services.obs.notifyObservers(null, "passwordmgr-crypto-login");
} else {
Services.obs.notifyObservers(null, "passwordmgr-crypto-loginCanceled");
}
return token.isLoggedIn();
},
/**
* Decrypts cipherText.
*
* @param {string} cipherText Encrypted string including the algorithm details.
* @param {boolean} reauth True if we want to force the prompt to show up
* even if the user is already logged in.
* @returns {Promise<string>} resolves to the decrypted string, or rejects otherwise.
*/
async decrypt(cipherText, reauth = false) {
if (!await this.ensureLoggedIn(reauth)) {
throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
}
return cryptoSDR.decrypt(cipherText);
},
/**
* Encrypts a string and returns cipher text containing algorithm information used for decryption.
*
* @param {string} plainText Original string without encryption.
* @returns {Promise<string>} resolves to the encrypted string (with algorithm), otherwise rejects.
*/
async encrypt(plainText) {
if (!await this.ensureLoggedIn()) {
throw Components.Exception("User canceled master password entry", Cr.NS_ERROR_ABORT);
}
return cryptoSDR.encrypt(plainText);
},
/**
* Resolve when master password dialogs are closed, immediately if none are open.
*
* An existing MP dialog will be focused and will request attention.
*
* @returns {Promise<boolean>}
* Resolves with whether the user is logged in to MP.
*/
async waitForExistingDialog() {
if (!this.isUIBusy) {
log.debug("waitForExistingDialog: Dialog isn't showing. isLoggedIn:", this.isLoggedIn);
return this.isLoggedIn;
}
return new Promise((resolve) => {
log.debug("waitForExistingDialog: Observing the open dialog");
let observer = {
QueryInterface: ChromeUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]),
observe(subject, topic, data) {
log.debug("waitForExistingDialog: Got notification:", topic);
// Only run observer once.
Services.obs.removeObserver(this, "passwordmgr-crypto-login");
Services.obs.removeObserver(this, "passwordmgr-crypto-loginCanceled");
if (topic == "passwordmgr-crypto-loginCanceled") {
resolve(false);
return;
}
resolve(true);
},
};
// Possible leak: it's possible that neither of these notifications
// will fire, and if that happens, we'll leak the observer (and
// never return). We should guarantee that at least one of these
// will fire.
// See bug XXX.
Services.obs.addObserver(observer, "passwordmgr-crypto-login");
Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled");
// Focus and draw attention to the existing master password dialog for the
// occassions where it's not attached to the current window.
let promptWin = Services.wm.getMostRecentWindow("prompt:promptPassword");
promptWin.focus();
promptWin.getAttention();
});
},
};
XPCOMUtils.defineLazyGetter(this, "log", () => {
let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
return new ConsoleAPI({
maxLogLevelPref: "masterPassword.loglevel",
prefix: "Master Password",
});
});

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

@ -1,251 +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/. */
/**
* Helpers for using OS Key Store.
*/
"use strict";
var EXPORTED_SYMBOLS = [
"OSKeyStore",
];
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "nativeOSKeyStore",
"@mozilla.org/security/oskeystore;1", Ci.nsIOSKeyStore);
// Skip reauth during tests, only works in non-official builds.
const TEST_ONLY_REAUTH = "extensions.formautofill.osKeyStore.unofficialBuildOnlyLogin";
var OSKeyStore = {
/**
* On macOS this becomes part of the name label visible on Keychain Acesss as
* "org.mozilla.nss.keystore.firefox" (where "firefox" is the MOZ_APP_NAME).
*/
STORE_LABEL: AppConstants.MOZ_APP_NAME,
/**
* Consider the module is initialized as locked. OS might unlock without a
* prompt.
* @type {Boolean}
*/
_isLocked: true,
_pendingUnlockPromise: null,
/**
* @returns {boolean} True if logged in (i.e. decrypt(reauth = false) will
* not retrigger a dialog) and false if not.
* User might log out elsewhere in the OS, so even if this
* is true a prompt might still pop up.
*/
get isLoggedIn() {
return !this._isLocked;
},
/**
* @returns {boolean} True if there is another login dialog existing and false
* otherwise.
*/
get isUIBusy() {
return !!this._pendingUnlockPromise;
},
/**
* If the test pref exist and applicable,
* this method will dispatch a observer message and return
* to simulate successful reauth, or throw to simulate
* failed reauth.
*
* @returns {boolean} True when reauth should NOT be skipped,
* false when reauth has been skipped.
* @throws If it needs to simulate reauth login failure.
*/
_maybeSkipReauthForTest() {
// Don't take test reauth pref in the following configurations.
if (nativeOSKeyStore.isNSSKeyStore ||
AppConstants.MOZILLA_OFFICIAL ||
!this._testReauth) {
return true;
}
// Skip this reauth because there is no way to mock the
// native dialog in the testing environment, for now.
log.debug("_ensureReauth: _testReauth: ", this._testReauth);
switch (this._testReauth) {
case "pass":
Services.obs.notifyObservers(null, "oskeystore-testonly-reauth", "pass");
return false;
case "cancel":
Services.obs.notifyObservers(null, "oskeystore-testonly-reauth", "cancel");
throw new Components.Exception("Simulating user cancelling login dialog", Cr.NS_ERROR_FAILURE);
default:
throw new Components.Exception("Unknown test pref value", Cr.NS_ERROR_FAILURE);
}
},
/**
* Ensure the store in use is logged in. It will display the OS login
* login prompt or do nothing if it's logged in already. If an existing login
* prompt is already prompted, the result from it will be used instead.
*
* Note: This method must set _pendingUnlockPromise before returning the
* promise (i.e. the first |await|), otherwise we'll risk re-entry.
* This is why there aren't an |await| in the method. The method is marked as
* |async| to communicate that it's async.
*
* @param {boolean} reauth Prompt the login dialog no matter it's logged in
* or not if it's set to true.
* @returns {Promise<boolean>} True if it's logged in or no password is set
* and false if it's still not logged in (prompt
* canceled or other error).
*/
async ensureLoggedIn(reauth = false) {
if (this._pendingUnlockPromise) {
log.debug("ensureLoggedIn: Has a pending unlock operation");
return this._pendingUnlockPromise;
}
log.debug("ensureLoggedIn: Creating new pending unlock promise. reauth: ", reauth);
// TODO: Implementing re-auth by passing this value to the native implementation
// in some way. Set this to false for now to ignore the reauth request (bug 1429265).
reauth = false;
let unlockPromise = Promise.resolve().then(async () => {
if (reauth) {
reauth = this._maybeSkipReauthForTest();
}
if (!await nativeOSKeyStore.asyncSecretAvailable(this.STORE_LABEL)) {
log.debug("ensureLoggedIn: Secret unavailable, attempt to generate new secret.");
let recoveryPhrase = await nativeOSKeyStore.asyncGenerateSecret(this.STORE_LABEL);
// TODO We should somehow have a dialog to ask the user to write this down,
// and another dialog somewhere for the user to restore the secret with it.
// (Intentionally not printing it out in the console)
log.debug("ensureLoggedIn: Secret generated. Recovery phrase length: " + recoveryPhrase.length);
}
});
if (nativeOSKeyStore.isNSSKeyStore) {
// Workaround bug 1492305: NSS-implemented methods don't reject when user cancels.
unlockPromise = unlockPromise.then(() => {
log.debug("ensureLoggedIn: isNSSKeyStore: ", reauth, Services.logins.isLoggedIn);
// User has hit the cancel button on the master password prompt.
// We must reject the promise chain here.
if (!Services.logins.isLoggedIn) {
throw Components.Exception("User canceled OS unlock entry (Workaround)", Cr.NS_ERROR_FAILURE);
}
});
}
unlockPromise = unlockPromise.then(() => {
log.debug("ensureLoggedIn: Logged in");
this._pendingUnlockPromise = null;
this._isLocked = false;
return true;
}, (err) => {
log.debug("ensureLoggedIn: Not logged in", err);
this._pendingUnlockPromise = null;
this._isLocked = true;
return false;
});
this._pendingUnlockPromise = unlockPromise;
return this._pendingUnlockPromise;
},
/**
* Decrypts cipherText.
*
* Note: In the event of an rejection, check the result property of the Exception
* object. Handles NS_ERROR_ABORT as user has cancelled the action (e.g.,
* don't show that dialog), apart from other errors (e.g., gracefully
* recover from that and still shows the dialog.)
*
* @param {string} cipherText Encrypted string including the algorithm details.
* @param {boolean} reauth True if we want to force the prompt to show up
* even if the user is already logged in.
* @returns {Promise<string>} resolves to the decrypted string, or rejects otherwise.
*/
async decrypt(cipherText, reauth = false) {
if (!await this.ensureLoggedIn(reauth)) {
throw Components.Exception("User canceled OS unlock entry", Cr.NS_ERROR_ABORT);
}
let bytes = await nativeOSKeyStore.asyncDecryptBytes(this.STORE_LABEL, cipherText);
return String.fromCharCode.apply(String, bytes);
},
/**
* Encrypts a string and returns cipher text containing algorithm information used for decryption.
*
* @param {string} plainText Original string without encryption.
* @returns {Promise<string>} resolves to the encrypted string (with algorithm), otherwise rejects.
*/
async encrypt(plainText) {
if (!await this.ensureLoggedIn()) {
throw Components.Exception("User canceled OS unlock entry", Cr.NS_ERROR_ABORT);
}
// Convert plain text into a UTF-8 binary string
plainText = unescape(encodeURIComponent(plainText));
// Convert it to an array
let textArr = [];
for (let char of plainText) {
textArr.push(char.charCodeAt(0));
}
let rawEncryptedText = await nativeOSKeyStore.asyncEncryptBytes(this.STORE_LABEL, textArr.length, textArr);
// Mark the output with a version number.
return rawEncryptedText;
},
/**
* Resolve when the login dialogs are closed, immediately if none are open.
*
* An existing MP dialog will be focused and will request attention.
*
* @returns {Promise<boolean>}
* Resolves with whether the user is logged in to MP.
*/
async waitForExistingDialog() {
if (this.isUIBusy) {
return this._pendingUnlockPromise;
}
return this.isLoggedIn;
},
/**
* Remove the store. For tests.
*/
async cleanup() {
return nativeOSKeyStore.asyncDeleteSecret(this.STORE_LABEL);
},
/**
* Check if the implementation is using the NSS key store.
* If so, tests will be able to handle the reauth dialog.
*/
get isNSSKeyStore() {
return nativeOSKeyStore.isNSSKeyStore;
},
};
XPCOMUtils.defineLazyGetter(this, "log", () => {
let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
return new ConsoleAPI({
maxLogLevelPref: "extensions.formautofill.loglevel",
prefix: "OSKeyStore",
});
});
XPCOMUtils.defineLazyPreferenceGetter(OSKeyStore, "_testReauth", TEST_ONLY_REAUTH, "");

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

@ -20,6 +20,7 @@
</fieldset>
<div id="controls-container">
<button id="remove" disabled="disabled" data-localization="removeBtnLabel"/>
<button id="show-hide-credit-cards" data-localization="showCreditCardsBtnLabel"/>
<!-- Wrapper is used to properly compute the search tooltip position -->
<div>
<button id="add" data-localization="addBtnLabel"/>
@ -33,6 +34,7 @@
records: document.getElementById("credit-cards"),
controlsContainer: document.getElementById("controls-container"),
remove: document.getElementById("remove"),
showHideCreditCards: document.getElementById("show-hide-credit-cards"),
add: document.getElementById("add"),
edit: document.getElementById("edit"),
});

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

@ -19,8 +19,8 @@ ChromeUtils.defineModuleGetter(this, "formAutofillStorage",
"resource://formautofill/FormAutofillStorage.jsm");
ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
"resource://formautofill/FormAutofillUtils.jsm");
ChromeUtils.defineModuleGetter(this, "OSKeyStore",
"resource://formautofill/OSKeyStore.jsm");
ChromeUtils.defineModuleGetter(this, "MasterPassword",
"resource://formautofill/MasterPassword.jsm");
this.log = null;
FormAutofill.defineLazyLogGetter(this, "manageAddresses");
@ -313,7 +313,11 @@ class ManageCreditCards extends ManageRecords {
elements.add.setAttribute("searchkeywords", FormAutofillUtils.EDIT_CREDITCARD_KEYWORDS
.map(key => FormAutofillUtils.stringBundle.GetStringFromName(key))
.join("\n"));
this._hasMasterPassword = MasterPassword.isEnabled;
this._isDecrypted = false;
if (this._hasMasterPassword) {
elements.showHideCreditCards.setAttribute("hidden", true);
}
}
/**
@ -322,24 +326,12 @@ class ManageCreditCards extends ManageRecords {
* @param {object} creditCard [optional]
*/
async openEditDialog(creditCard) {
// Ask for reauth if user is trying to edit an existing credit card.
if (!creditCard || await OSKeyStore.ensureLoggedIn(true)) {
// If master password is set, ask for password if user is trying to edit an
// existing credit card.
if (!creditCard || !this._hasMasterPassword || await MasterPassword.ensureLoggedIn(true)) {
let decryptedCCNumObj = {};
if (creditCard) {
try {
decryptedCCNumObj["cc-number"] = await OSKeyStore.decrypt(creditCard["cc-number-encrypted"]);
} catch (ex) {
if (ex.result == Cr.NS_ERROR_ABORT) {
// User shouldn't be ask to reauth here, but it could happen.
// Return here and skip opening the dialog.
return;
}
// We've got ourselves a real error.
// Recover from encryption error so the user gets a chance to re-enter
// unencrypted credit card number.
decryptedCCNumObj["cc-number"] = "";
Cu.reportError(ex);
}
decryptedCCNumObj["cc-number"] = await MasterPassword.decrypt(creditCard["cc-number-encrypted"]);
}
let decryptedCreditCard = Object.assign({}, creditCard, decryptedCCNumObj);
this.prefWin.gSubDialog.open(EDIT_CREDIT_CARD_URL, "resizable=no", {
@ -367,6 +359,12 @@ class ManageCreditCards extends ManageRecords {
return cardObj.getLabel({showNumbers: showCreditCards});
}
async toggleShowHideCards(options) {
this._isDecrypted = !this._isDecrypted;
this.updateShowHideButtonState();
await this.updateLabels(options, this._isDecrypted);
}
async updateLabels(options, isDecrypted) {
for (let option of options) {
option.text = await this.getLabel(option.record, isDecrypted);
@ -394,10 +392,25 @@ class ManageCreditCards extends ManageRecords {
}
updateButtonsStates(selectedCount) {
this.updateShowHideButtonState();
super.updateButtonsStates(selectedCount);
}
updateShowHideButtonState() {
if (this._elements.records.length) {
this._elements.showHideCreditCards.removeAttribute("disabled");
} else {
this._elements.showHideCreditCards.setAttribute("disabled", true);
}
this._elements.showHideCreditCards.textContent =
this._isDecrypted ? FormAutofillUtils.stringBundle.GetStringFromName("hideCreditCardsBtnLabel") :
FormAutofillUtils.stringBundle.GetStringFromName("showCreditCardsBtnLabel");
}
handleClick(event) {
if (event.target == this._elements.showHideCreditCards) {
this.toggleShowHideCards(this._elements.records.options);
}
super.handleClick(event);
}
}

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

@ -104,6 +104,8 @@ manageCreditCardsTitle = Saved Credit Cards
# in browser preferences.
addressesListHeader = Addresses
creditCardsListHeader = Credit Cards
showCreditCardsBtnLabel = Show Credit Cards
hideCreditCardsBtnLabel = Hide Credit Cards
removeBtnLabel = Remove
addBtnLabel = Add…
editBtnLabel = Edit…

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

@ -32,8 +32,6 @@ elif CONFIG['OS_ARCH'] == 'WINNT':
'skin/windows/editDialog.css',
]
TESTING_JS_MODULES += ['test/fixtures/OSKeyStoreTestUtils.jsm']
BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini']
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']

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

@ -15,8 +15,7 @@ skip-if = (verify && (os == 'win' || os == 'mac'))
[browser_check_installed.js]
[browser_creditCard_doorhanger.js]
skip-if = (os == "linux") || (os == "mac" && debug) || (os == "win") # bug 1425884
[browser_creditCard_fill_cancel_login.js]
skip-if = true # Re-auth is not implemented, cannot cancel OS key store login (bug 1429265)
[browser_creditCard_fill_master_password.js]
[browser_dropdown_layout.js]
[browser_editAddressDialog.js]
[browser_editCreditCardDialog.js]

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

@ -51,7 +51,6 @@ add_task(async function test_submit_creditCard_saved() {
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
let onChanged = TestUtils.topicObserved("formautofill-storage-changed");
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
@ -70,7 +69,6 @@ add_task(async function test_submit_creditCard_saved() {
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
await onChanged;
}
);
@ -84,11 +82,6 @@ add_task(async function test_submit_creditCard_saved() {
});
add_task(async function test_submit_untouched_creditCard_form() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), "Cannot test OS key store login on official builds.");
return;
}
await SpecialPowers.pushPrefEnv({
"set": [
[CREDITCARDS_USED_STATUS_PREF, 0],
@ -97,16 +90,11 @@ add_task(async function test_submit_untouched_creditCard_form() {
await saveCreditCard(TEST_CREDIT_CARD_1);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
let onUsed = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "notifyUsed");
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
await openPopupOn(browser, "form #cc-name");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await osKeyStoreLoginShown;
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
@ -119,7 +107,6 @@ add_task(async function test_submit_untouched_creditCard_form() {
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
}
);
await onUsed;
creditCards = await getCreditCards();
is(creditCards.length, 1, "Still 1 credit card");
@ -138,9 +125,6 @@ add_task(async function test_submit_changed_subset_creditCard_form() {
await saveCreditCard(TEST_CREDIT_CARD_1);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let onUsed = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "notifyUsed");
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
@ -165,7 +149,6 @@ add_task(async function test_submit_changed_subset_creditCard_form() {
await clickDoorhangerButton(MAIN_BUTTON);
}
);
await onUsed;
creditCards = await getCreditCards();
is(creditCards.length, 1, "Still 1 credit card in storage");
@ -291,6 +274,78 @@ add_task(async function test_submit_creditCard_never_save() {
SpecialPowers.clearUserPref(ENABLED_AUTOFILL_CREDITCARDS_PREF);
});
add_task(async function test_submit_creditCard_saved_with_mp_enabled() {
LoginTestUtils.masterPassword.enable();
// Login with the masterPassword in LoginTestUtils.
let masterPasswordDialogShown = waitForMasterPasswordDialog(true);
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.focus();
await new Promise(resolve => setTimeout(resolve, 1000));
name.setUserInput("User 0");
let number = form.querySelector("#cc-number");
number.setUserInput("6387060366272981");
// Wait 1000ms before submission to make sure the input value applied
await new Promise(resolve => setTimeout(resolve, 1000));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
await masterPasswordDialogShown;
await TestUtils.topicObserved("formautofill-storage-changed");
}
);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
is(creditCards[0]["cc-name"], "User 0", "Verify the name field");
is(creditCards[0]["cc-number"], "************2981", "Verify the card number field");
LoginTestUtils.masterPassword.disable();
await removeAllRecords();
});
add_task(async function test_submit_creditCard_saved_with_mp_enabled_but_canceled() {
LoginTestUtils.masterPassword.enable();
let masterPasswordDialogShown = waitForMasterPasswordDialog();
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.focus();
await new Promise(resolve => setTimeout(resolve, 1000));
name.setUserInput("User 2");
let number = form.querySelector("#cc-number");
number.setUserInput("5471839082338112");
// Wait 1000ms before submission to make sure the input value applied
await new Promise(resolve => setTimeout(resolve, 1000));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
await masterPasswordDialogShown;
}
);
await sleep(1000);
let creditCards = await getCreditCards();
is(creditCards.length, 0, "No credit cards in storage");
LoginTestUtils.masterPassword.disable();
});
add_task(async function test_submit_creditCard_with_sync_account() {
await SpecialPowers.pushPrefEnv({
"set": [
@ -390,8 +445,6 @@ add_task(async function test_submit_manual_mergeable_creditCard_form() {
await saveCreditCard(TEST_CREDIT_CARD_3);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let onUsed = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "notifyUsed");
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
@ -414,7 +467,6 @@ add_task(async function test_submit_manual_mergeable_creditCard_form() {
await clickDoorhangerButton(MAIN_BUTTON);
}
);
await onUsed;
creditCards = await getCreditCards();
is(creditCards.length, 1, "Still 1 credit card in storage");
@ -425,11 +477,6 @@ add_task(async function test_submit_manual_mergeable_creditCard_form() {
});
add_task(async function test_update_autofill_form_name() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), "Cannot test OS key store login on official builds.");
return;
}
await SpecialPowers.pushPrefEnv({
"set": [
[CREDITCARDS_USED_STATUS_PREF, 0],
@ -438,9 +485,6 @@ add_task(async function test_update_autofill_form_name() {
await saveCreditCard(TEST_CREDIT_CARD_1);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
let onUsed = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "notifyUsed");
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
@ -448,13 +492,7 @@ add_task(async function test_update_autofill_form_name() {
await openPopupOn(browser, "form #cc-name");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await osKeyStoreLoginShown;
await ContentTask.spawn(browser, null, async function() {
await ContentTaskUtils.waitForCondition(() => {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
return name.value == "John Doe";
}, "Credit card detail never fills");
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.setUserInput("User 1");
@ -463,11 +501,11 @@ add_task(async function test_update_autofill_form_name() {
await new Promise(resolve => setTimeout(resolve, 1000));
form.querySelector("input[type=submit]").click();
});
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
}
);
await onUsed;
creditCards = await getCreditCards();
is(creditCards.length, 1, "Still 1 credit card");
@ -479,11 +517,6 @@ add_task(async function test_update_autofill_form_name() {
});
add_task(async function test_update_autofill_form_exp_date() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), "Cannot test OS key store login on official builds.");
return;
}
await SpecialPowers.pushPrefEnv({
"set": [
[CREDITCARDS_USED_STATUS_PREF, 0],
@ -492,9 +525,6 @@ add_task(async function test_update_autofill_form_exp_date() {
await saveCreditCard(TEST_CREDIT_CARD_1);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
let onUsed = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "notifyUsed");
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
@ -503,11 +533,6 @@ add_task(async function test_update_autofill_form_exp_date() {
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await ContentTask.spawn(browser, null, async function() {
await ContentTaskUtils.waitForCondition(() => {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
return name.value == "John Doe";
}, "Credit card detail never fills");
let form = content.document.getElementById("form");
let year = form.querySelector("#cc-exp-year");
year.setUserInput("2020");
@ -519,10 +544,8 @@ add_task(async function test_update_autofill_form_exp_date() {
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
await osKeyStoreLoginShown;
}
);
await onUsed;
creditCards = await getCreditCards();
is(creditCards.length, 1, "Still 1 credit card");
@ -534,11 +557,6 @@ add_task(async function test_update_autofill_form_exp_date() {
});
add_task(async function test_create_new_autofill_form() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), "Cannot test OS key store login on official builds.");
return;
}
await SpecialPowers.pushPrefEnv({
"set": [
[CREDITCARDS_USED_STATUS_PREF, 0],
@ -547,21 +565,14 @@ add_task(async function test_create_new_autofill_form() {
await saveCreditCard(TEST_CREDIT_CARD_1);
let creditCards = await getCreditCards();
is(creditCards.length, 1, "1 credit card in storage");
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
let onChanged = TestUtils.topicObserved("formautofill-storage-changed");
await openPopupOn(browser, "form #cc-name");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await ContentTask.spawn(browser, null, async function() {
await ContentTaskUtils.waitForCondition(() => {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
return name.value == "John Doe";
}, "Credit card detail never fills");
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
name.setUserInput("User 1");
@ -573,8 +584,6 @@ add_task(async function test_create_new_autofill_form() {
await promiseShown;
await clickDoorhangerButton(SECONDARY_BUTTON);
await osKeyStoreLoginShown;
await onChanged;
}
);
@ -589,11 +598,6 @@ add_task(async function test_create_new_autofill_form() {
});
add_task(async function test_update_duplicate_autofill_form() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), "Cannot test OS key store login on official builds.");
return;
}
await SpecialPowers.pushPrefEnv({
"set": [
[CREDITCARDS_USED_STATUS_PREF, 0],
@ -607,24 +611,16 @@ add_task(async function test_update_duplicate_autofill_form() {
});
let creditCards = await getCreditCards();
is(creditCards.length, 2, "2 credit card in storage");
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(true);
let onUsed = TestUtils.topicObserved("formautofill-storage-changed",
(subject, data) => data == "notifyUsed");
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
await openPopupOn(browser, "form #cc-number");
await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
await ContentTask.spawn(browser, null, async function() {
await ContentTaskUtils.waitForCondition(() => {
let form = content.document.getElementById("form");
let number = form.querySelector("#cc-number");
return number.value == "6387060366272981";
}, "Should be the first credit card number");
// Change number to the second credit card number
let form = content.document.getElementById("form");
let number = form.querySelector("#cc-number");
is(number.value, "6387060366272981", "Should be the first credit card number");
// Change number to the second credit card number
number.setUserInput("5038146897157463");
// Wait 1000ms before submission to make sure the input value applied
@ -634,10 +630,8 @@ add_task(async function test_update_duplicate_autofill_form() {
await sleep(1000);
is(PopupNotifications.panel.state, "closed", "Doorhanger is hidden");
await osKeyStoreLoginShown;
}
);
await onUsed;
creditCards = await getCreditCards();
is(creditCards.length, 2, "Still 2 credit card");
@ -652,7 +646,6 @@ add_task(async function test_submit_creditCard_with_invalid_network() {
async function(browser) {
let promiseShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel,
"popupshown");
let onChanged = TestUtils.topicObserved("formautofill-storage-changed");
await ContentTask.spawn(browser, null, async function() {
let form = content.document.getElementById("form");
let name = form.querySelector("#cc-name");
@ -671,7 +664,6 @@ add_task(async function test_submit_creditCard_with_invalid_network() {
await promiseShown;
await clickDoorhangerButton(MAIN_BUTTON);
await onChanged;
}
);

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

@ -1,20 +1,20 @@
"use strict";
add_task(async function test_fill_creditCard_but_cancel_login() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), "Cannot test OS key store login on official builds.");
return;
}
add_task(async function test_fill_creditCard_with_mp_enabled_but_canceled() {
await saveCreditCard(TEST_CREDIT_CARD_2);
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(false); // cancel
LoginTestUtils.masterPassword.enable();
registerCleanupFunction(() => {
LoginTestUtils.masterPassword.disable();
});
let masterPasswordDialogShown = waitForMasterPasswordDialog(false); // cancel
await BrowserTestUtils.withNewTab({gBrowser, url: CREDITCARD_FORM_URL},
async function(browser) {
await openPopupOn(browser, "#cc-name");
const ccItem = getDisplayedPopupItems(browser)[0];
await EventUtils.synthesizeMouseAtCenter(ccItem, {});
await Promise.all([osKeyStoreLoginShown, expectPopupClose(browser)]);
await Promise.all([masterPasswordDialogShown, expectPopupClose(browser)]);
await ContentTask.spawn(browser, {}, async function() {
is(content.document.querySelector("#cc-name").value, "", "Check name");

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

@ -3,6 +3,7 @@
const TEST_SELECTORS = {
selRecords: "#credit-cards",
btnRemove: "#remove",
btnShowHideCreditCards: "#show-hide-credit-cards",
btnAdd: "#add",
btnEdit: "#edit",
};
@ -14,11 +15,13 @@ add_task(async function test_manageCreditCardsInitialState() {
await ContentTask.spawn(browser, TEST_SELECTORS, (args) => {
let selRecords = content.document.querySelector(args.selRecords);
let btnRemove = content.document.querySelector(args.btnRemove);
let btnShowHideCreditCards = content.document.querySelector(args.btnShowHideCreditCards);
let btnAdd = content.document.querySelector(args.btnAdd);
let btnEdit = content.document.querySelector(args.btnEdit);
is(selRecords.length, 0, "No credit card");
is(btnRemove.disabled, true, "Remove button disabled");
is(btnShowHideCreditCards.disabled, true, "Show Credit Cards button disabled");
is(btnAdd.disabled, false, "Add button enabled");
is(btnEdit.disabled, true, "Edit button disabled");
});
@ -104,6 +107,57 @@ add_task(async function test_creditCardsDialogWatchesStorageChanges() {
win.close();
});
add_task(async function test_showCreditCards() {
await SpecialPowers.pushPrefEnv({"set": [["privacy.reduceTimerPrecision", false]]});
await saveCreditCard(TEST_CREDIT_CARD_1);
await saveCreditCard(TEST_CREDIT_CARD_2);
await saveCreditCard(TEST_CREDIT_CARD_3);
let win = window.openDialog(MANAGE_CREDIT_CARDS_DIALOG_URL, null, DIALOG_SIZE);
await waitForFocusAndFormReady(win);
let selRecords = win.document.querySelector(TEST_SELECTORS.selRecords);
let btnShowHideCreditCards = win.document.querySelector(TEST_SELECTORS.btnShowHideCreditCards);
is(btnShowHideCreditCards.disabled, false, "Show credit cards button enabled");
is(btnShowHideCreditCards.textContent, "Show Credit Cards", "Label should be 'Show Credit Cards'");
// Show credit card numbers
EventUtils.synthesizeMouseAtCenter(btnShowHideCreditCards, {}, win);
await BrowserTestUtils.waitForEvent(selRecords, "LabelsUpdated");
is(selRecords[0].text, "5103059495477870", "Decrypted credit card 3");
is(selRecords[1].text, "4929001587121045, Timothy Berners-Lee", "Decrypted credit card 2");
is(selRecords[2].text, "4111111111111111, John Doe", "Decrypted credit card 1");
is(btnShowHideCreditCards.textContent, "Hide Credit Cards", "Label should be 'Hide Credit Cards'");
// Hide credit card numbers
EventUtils.synthesizeMouseAtCenter(btnShowHideCreditCards, {}, win);
await BrowserTestUtils.waitForEvent(selRecords, "LabelsUpdated");
is(selRecords[0].text, "**** 7870", "Masked credit card 3");
is(selRecords[1].text, "**** 1045, Timothy Berners-Lee", "Masked credit card 2");
is(selRecords[2].text, "**** 1111, John Doe", "Masked credit card 1");
is(btnShowHideCreditCards.textContent, "Show Credit Cards", "Label should be 'Show Credit Cards'");
// Show credit card numbers again to test if they revert back to masked form when reloaded
EventUtils.synthesizeMouseAtCenter(btnShowHideCreditCards, {}, win);
await BrowserTestUtils.waitForEvent(selRecords, "LabelsUpdated");
// Ensure credit card numbers are shown again
is(selRecords[0].text, "5103059495477870", "Decrypted credit card 3");
// Remove a card to trigger reloading
await removeCreditCards([selRecords.options[2].value]);
await BrowserTestUtils.waitForEvent(selRecords, "RecordsLoaded");
is(selRecords[0].text, "**** 7870", "Masked credit card 3");
is(selRecords[1].text, "**** 1045, Timothy Berners-Lee", "Masked credit card 2");
// Remove the rest of the cards
await removeCreditCards([selRecords.options[1].value]);
await removeCreditCards([selRecords.options[0].value]);
await BrowserTestUtils.waitForEvent(selRecords, "RecordsLoaded");
is(btnShowHideCreditCards.disabled, true, "Show credit cards button is disabled when there is no card");
win.close();
});
add_task(async function test_showCreditCardIcons() {
await SpecialPowers.pushPrefEnv({"set": [["privacy.reduceTimerPrecision", false]]});
await saveCreditCard(TEST_CREDIT_CARD_1);
@ -139,41 +193,35 @@ add_task(async function test_showCreditCardIcons() {
win.close();
});
add_task(async function test_hasEditLoginPrompt() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(OSKeyStoreTestUtils.canTestOSKeyStoreLogin(), "Cannot test OS key store login on official builds.");
return;
}
add_task(async function test_hasMasterPassword() {
await saveCreditCard(TEST_CREDIT_CARD_1);
LoginTestUtils.masterPassword.enable();
let win = window.openDialog(MANAGE_CREDIT_CARDS_DIALOG_URL, null, DIALOG_SIZE);
await waitForFocusAndFormReady(win);
let selRecords = win.document.querySelector(TEST_SELECTORS.selRecords);
let btnRemove = win.document.querySelector(TEST_SELECTORS.btnRemove);
let btnShowHideCreditCards = win.document.querySelector(TEST_SELECTORS.btnShowHideCreditCards);
let btnAdd = win.document.querySelector(TEST_SELECTORS.btnAdd);
// let btnEdit = win.document.querySelector(TEST_SELECTORS.btnEdit);
let btnEdit = win.document.querySelector(TEST_SELECTORS.btnEdit);
let masterPasswordDialogShown = waitForMasterPasswordDialog();
is(btnShowHideCreditCards.hidden, true, "Show credit cards button is hidden");
// Master password dialog should show when trying to edit a credit card record.
EventUtils.synthesizeMouseAtCenter(selRecords.children[0], {}, win);
// Login dialog should show when trying to edit a credit card record.
// TODO: test disabled because re-auth is not implemented yet (bug 1429265).
/*
let osKeyStoreLoginShown = OSKeyStoreTestUtils.waitForOSKeyStoreLogin(); // cancel
EventUtils.synthesizeMouseAtCenter(btnEdit, {}, win);
await osKeyStoreLoginShown;
await new Promise(resolve => waitForFocus(resolve, win));
await new Promise(resolve => executeSoon(resolve));
*/
await masterPasswordDialogShown;
// Login is not required for removing credit cards.
// Master password is not required for removing credit cards.
EventUtils.synthesizeMouseAtCenter(btnRemove, {}, win);
await BrowserTestUtils.waitForEvent(selRecords, "RecordsRemoved");
is(selRecords.length, 0, "Credit card is removed");
// gSubDialog.open should be called when trying to add a credit card,
// no OS login dialog is required.
// no master password is required.
window.gSubDialog = {
open: url => is(url, EDIT_CREDIT_CARD_DIALOG_URL, "Edit credit card dialog is called"),
};

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

@ -8,13 +8,13 @@
DEFAULT_REGION_PREF,
sleep, expectPopupOpen, openPopupOn, expectPopupClose, closePopup, clickDoorhangerButton,
getAddresses, saveAddress, removeAddresses, saveCreditCard,
getDisplayedPopupItems, getDoorhangerCheckbox,
getDisplayedPopupItems, getDoorhangerCheckbox, waitForMasterPasswordDialog,
getNotification, getDoorhangerButton, removeAllRecords, testDialog */
"use strict";
ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", this);
ChromeUtils.import("resource://testing-common/OSKeyStoreTestUtils.jsm", this);
ChromeUtils.import("resource://testing-common/LoginTestUtils.jsm", this);
ChromeUtils.import("resource://formautofill/MasterPassword.jsm", this);
const MANAGE_ADDRESSES_DIALOG_URL = "chrome://formautofill/content/manageAddresses.xhtml";
const MANAGE_CREDIT_CARDS_DIALOG_URL = "chrome://formautofill/content/manageCreditCards.xhtml";
@ -22,7 +22,8 @@ const EDIT_ADDRESS_DIALOG_URL = "chrome://formautofill/content/editAddress.xhtml
const EDIT_CREDIT_CARD_DIALOG_URL = "chrome://formautofill/content/editCreditCard.xhtml";
const BASE_URL = "http://mochi.test:8888/browser/browser/extensions/formautofill/test/browser/";
const FORM_URL = "http://mochi.test:8888/browser/browser/extensions/formautofill/test/browser/autocomplete_basic.html";
const CREDITCARD_FORM_URL = "https://example.org/browser/browser/extensions/formautofill/test/browser/autocomplete_creditcard_basic.html";
const CREDITCARD_FORM_URL =
"https://example.org/browser/browser/extensions/formautofill/test/browser/autocomplete_creditcard_basic.html";
const FTU_PREF = "extensions.formautofill.firstTimeUse";
const CREDITCARDS_USED_STATUS_PREF = "extensions.formautofill.creditCards.used";
const ENABLED_AUTOFILL_ADDRESSES_PREF = "extensions.formautofill.addresses.enabled";
@ -325,6 +326,24 @@ function getDoorhangerButton(button) {
return getNotification()[button];
}
// Wait for the master password dialog to popup and enter the password to log in
// if "login" is "true" or dismiss it directly if otherwise.
function waitForMasterPasswordDialog(login = false) {
info("expecting master password dialog loaded");
let dialogShown = TestUtils.topicObserved("common-dialog-loaded");
return dialogShown.then(([subject]) => {
let dialog = subject.Dialog;
is(dialog.args.title, "Password Required", "Master password dialog shown");
if (login) {
dialog.ui.password1Textbox.value = LoginTestUtils.masterPassword.masterPassword;
dialog.ui.button0.click();
} else {
dialog.ui.button1.click();
}
});
}
async function removeAllRecords() {
let addresses = await getAddresses();
if (addresses.length) {
@ -347,7 +366,7 @@ async function waitForFocusAndFormReady(win) {
async function testDialog(url, testFn, arg = undefined) {
if (url == EDIT_CREDIT_CARD_DIALOG_URL && arg && arg.record) {
arg.record = Object.assign({}, arg.record, {
"cc-number": await OSKeyStore.decrypt(arg.record["cc-number-encrypted"]),
"cc-number": await MasterPassword.decrypt(arg.record["cc-number-encrypted"]),
});
}
let win = window.openDialog(url, null, "width=600,height=600", arg);
@ -357,11 +376,4 @@ async function testDialog(url, testFn, arg = undefined) {
return unloadPromise;
}
add_task(function setup() {
OSKeyStoreTestUtils.setup();
});
registerCleanupFunction(removeAllRecords);
registerCleanupFunction(async () => {
await OSKeyStoreTestUtils.cleanup();
});

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

@ -1,93 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var EXPORTED_SYMBOLS = [
"OSKeyStoreTestUtils",
];
ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", this);
// TODO: Consider AppConstants.MOZILLA_OFFICIAL to decide if we could test re-auth (bug 1429265).
/*
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
*/
ChromeUtils.import("resource://testing-common/LoginTestUtils.jsm", this);
ChromeUtils.import("resource://testing-common/TestUtils.jsm");
var OSKeyStoreTestUtils = {
/*
TEST_ONLY_REAUTH: "extensions.formautofill.osKeyStore.unofficialBuildOnlyLogin",
*/
setup() {
// TODO: run tests with master password enabled to ensure NSS-implemented
// key store prompts on re-auth (bug 1429265)
/*
LoginTestUtils.masterPassword.enable();
*/
this.ORIGINAL_STORE_LABEL = OSKeyStore.STORE_LABEL;
OSKeyStore.STORE_LABEL = "test-" + Math.random().toString(36).substr(2);
},
async cleanup() {
// TODO: run tests with master password enabled to ensure NSS-implemented
// key store prompts on re-auth (bug 1429265)
/*
LoginTestUtils.masterPassword.disable();
*/
await OSKeyStore.cleanup();
OSKeyStore.STORE_LABEL = this.ORIGINAL_STORE_LABEL;
},
canTestOSKeyStoreLogin() {
// TODO: return true based on whether or not we could test the prompt on
// the platform (bug 1429265).
/*
return OSKeyStore.isNSSKeyStore || !AppConstants.MOZILLA_OFFICIAL;
*/
return true;
},
// Wait for the master password dialog to popup and enter the password to log in
// if "login" is "true" or dismiss it directly if otherwise.
async waitForOSKeyStoreLogin(login = false) {
// TODO: Always resolves for now, because we are skipping re-auth on all
// platforms (bug 1429265).
/*
if (OSKeyStore.isNSSKeyStore) {
await this.waitForMasterPasswordDialog(login);
return;
}
const str = login ? "pass" : "cancel";
Services.prefs.setStringPref(this.TEST_ONLY_REAUTH, str);
await TestUtils.topicObserved("oskeystore-testonly-reauth",
(subject, data) => data == str);
Services.prefs.setStringPref(this.TEST_ONLY_REAUTH, "");
*/
},
async waitForMasterPasswordDialog(login = false) {
let [subject] = await TestUtils.topicObserved("common-dialog-loaded");
let dialog = subject.Dialog;
if (dialog.args.title !== "Password Required") {
throw new Error("Incorrect master password dialog title");
}
if (login) {
dialog.ui.password1Textbox.value = LoginTestUtils.masterPassword.masterPassword;
dialog.ui.button0.click();
} else {
dialog.ui.button1.click();
}
await TestUtils.waitForTick();
},
};

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

@ -1,6 +1,5 @@
/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SimpleTest.js */
/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/EventUtils.js */
/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/AddTask.js */
/* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
/* eslint-disable no-unused-vars */
@ -202,15 +201,6 @@ async function cleanUpStorage() {
await cleanUpCreditCards();
}
async function canTestOSKeyStoreLogin() {
let {canTest} = await invokeAsyncChromeTask("FormAutofillTest:CanTestOSKeyStoreLogin", "FormAutofillTest:CanTestOSKeyStoreLoginResult");
return canTest;
}
async function waitForOSKeyStoreLogin(login = false) {
await invokeAsyncChromeTask("FormAutofillTest:OSKeyStoreLogin", "FormAutofillTest:OSKeyStoreLoggedIn", {login});
}
function patchRecordCCNumber(record) {
const number = record["cc-number"];
const ccNumberFmt = {
@ -273,12 +263,6 @@ function formAutoFillCommonSetup() {
}
});
add_task(async function setup() {
formFillChromeScript.sendAsyncMessage("setup");
info(`expecting the storage setup`);
await formFillChromeScript.promiseOneMessage("setup-finished");
});
SimpleTest.registerCleanupFunction(async () => {
formFillChromeScript.sendAsyncMessage("cleanup");
info(`expecting the storage cleanup`);

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

@ -4,11 +4,8 @@
"use strict";
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
ChromeUtils.import("resource://formautofill/OSKeyStore.jsm");
ChromeUtils.import("resource://testing-common/OSKeyStoreTestUtils.jsm");
let {formAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
@ -117,15 +114,9 @@ var ParentUtils = {
await this.operateCreditCard("remove", {guids}, "FormAutofillTest:CreditCardsCleanedUp");
},
setup() {
OSKeyStoreTestUtils.setup();
},
async cleanup() {
await this.cleanUpAddresses();
await this.cleanUpCreditCards();
await OSKeyStoreTestUtils.cleanup();
Services.obs.removeObserver(this, "formautofill-storage-changed");
},
@ -224,23 +215,8 @@ addMessageListener("FormAutofillTest:CleanUpCreditCards", (msg) => {
ParentUtils.cleanUpCreditCards();
});
addMessageListener("FormAutofillTest:CanTestOSKeyStoreLogin", (msg) => {
sendAsyncMessage("FormAutofillTest:CanTestOSKeyStoreLoginResult",
{canTest: OSKeyStore.isNSSKeyStore || !AppConstants.MOZILLA_OFFICIAL});
});
addMessageListener("FormAutofillTest:OSKeyStoreLogin", async (msg) => {
await OSKeyStoreTestUtils.waitForOSKeyStoreLogin(msg.login);
sendAsyncMessage("FormAutofillTest:OSKeyStoreLoggedIn");
});
addMessageListener("setup", async () => {
ParentUtils.setup();
sendAsyncMessage("setup-finished", {});
});
addMessageListener("cleanup", async () => {
await ParentUtils.cleanup();
sendAsyncMessage("cleanup-finished", {});
addMessageListener("cleanup", () => {
ParentUtils.cleanup().then(() => {
sendAsyncMessage("cleanup-finished", {});
});
});

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

@ -150,16 +150,8 @@ add_task(async function check_search_result_for_pref_off() {
await SpecialPowers.popPrefEnv();
});
let canTest;
// Autofill the credit card from dropdown menu.
add_task(async function check_fields_after_form_autofill() {
canTest = await canTestOSKeyStoreLogin();
if (!canTest) {
todo(canTest, "Cannot test OS key store login on official builds.");
return;
}
await setInput("#cc-exp-year", 202);
synthesizeKey("KEY_ArrowDown");
@ -170,18 +162,11 @@ add_task(async function check_fields_after_form_autofill() {
})));
synthesizeKey("KEY_ArrowDown");
let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
await new Promise(resolve => SimpleTest.executeSoon(resolve));
await triggerAutofillAndCheckProfile(MOCK_STORAGE[1]);
await osKeyStoreLoginShown;
});
// Fallback to history search after autofill address.
add_task(async function check_fallback_after_form_autofill() {
if (!canTest) {
return;
}
await setInput("#cc-name", "", true);
synthesizeKey("KEY_ArrowDown");
await expectPopup();
@ -190,10 +175,6 @@ add_task(async function check_fallback_after_form_autofill() {
// Resume form autofill once all the autofilled fileds are changed.
add_task(async function check_form_autofill_resume() {
if (!canTest) {
return;
}
document.querySelector("#cc-name").blur();
document.querySelector("#form1").reset();

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

@ -103,16 +103,9 @@ add_task(async function clear_modified_form() {
});
add_task(async function clear_distinct_section() {
if (!(await canTestOSKeyStoreLogin())) {
todo(false, "Cannot test OS key store login on official builds.");
return;
}
document.getElementById("form1").reset();
await triggerPopupAndHoverItem("#cc-name", 0);
let osKeyStoreLoginShown = waitForOSKeyStoreLogin(true);
await triggerAutofillAndCheckProfile(MOCK_CC_STORAGE[0]);
await osKeyStoreLoginShown;
await triggerPopupAndHoverItem("#organization", 0);
await triggerAutofillAndCheckProfile(MOCK_ADDR_STORAGE[0]);

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

@ -99,7 +99,7 @@ async function initProfileStorage(fileName, records, collectionName = "addresses
subject.wrappedJSObject.collectionName == collectionName
);
for (let record of records) {
Assert.ok(await profileStorage[collectionName].add(record));
Assert.ok(profileStorage[collectionName].add(record));
await onChanged;
}
await profileStorage._saveImmediately();
@ -223,13 +223,3 @@ add_task(async function head_initialize() {
await loadExtension();
});
let OSKeyStoreTestUtils;
add_task(async function os_key_store_setup() {
({OSKeyStoreTestUtils} =
ChromeUtils.import("resource://testing-common/OSKeyStoreTestUtils.jsm", {}));
OSKeyStoreTestUtils.setup();
registerCleanupFunction(async function cleanup() {
await OSKeyStoreTestUtils.cleanup();
});
});

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

@ -5,9 +5,10 @@
"use strict";
let MasterPassword;
add_task(async function setup() {
ChromeUtils.import("resource://formautofill/FormAutofillHandler.jsm");
ChromeUtils.import("resource://formautofill/OSKeyStore.jsm");
({MasterPassword} = ChromeUtils.import("resource://formautofill/MasterPassword.jsm", {}));
});
const TESTCASES = [
@ -476,7 +477,7 @@ function do_test(testcases, testFn) {
info("Starting testcase: " + testcase.description);
let ccNumber = testcase.profileData["cc-number"];
if (ccNumber) {
testcase.profileData["cc-number-encrypted"] = await OSKeyStore.encrypt(ccNumber);
testcase.profileData["cc-number-encrypted"] = await MasterPassword.encrypt(ccNumber);
delete testcase.profileData["cc-number"];
}
@ -486,11 +487,18 @@ function do_test(testcases, testFn) {
let formLike = FormLikeFactory.createFromForm(form);
let handler = new FormAutofillHandler(formLike);
let promises = [];
// Replace the internal decrypt method with OSKeyStore API,
// but don't pass the reauth parameter to avoid triggering
// reauth login dialog in these tests.
// Replace the interal decrypt method with MasterPassword API
let decryptHelper = async (cipherText, reauth) => {
return OSKeyStore.decrypt(cipherText, false);
let string;
try {
string = await MasterPassword.decrypt(cipherText, reauth);
} catch (e) {
if (e.result != Cr.NS_ERROR_ABORT) {
throw e;
}
info("User canceled master password entry");
}
return string;
};
handler.collectFormFields();

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

@ -316,7 +316,7 @@ add_task(async function test_add() {
do_check_credit_card_matches(creditCards[1], TEST_CREDIT_CARD_2);
Assert.notEqual(creditCards[0].guid, undefined);
Assert.equal(creditCards[0].version, 2);
Assert.equal(creditCards[0].version, 1);
Assert.notEqual(creditCards[0].timeCreated, undefined);
Assert.equal(creditCards[0].timeLastModified, creditCards[0].timeCreated);
Assert.equal(creditCards[0].timeLastUsed, 0);
@ -422,14 +422,6 @@ add_task(async function test_update() {
Assert.equal(creditCard["cc-number"],
CreditCard.getLongMaskedNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
// Decryption failure of existing record should not prevent it from being updated.
creditCard = profileStorage.creditCards._data[0];
creditCard["cc-number-encrypted"] = "INVALID";
await profileStorage.creditCards.update(profileStorage.creditCards._data[0].guid, TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD, false);
creditCard = profileStorage.creditCards._data[0];
Assert.equal(creditCard["cc-number"],
CreditCard.getLongMaskedNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
await Assert.rejects(profileStorage.creditCards.update("INVALID_GUID", TEST_CREDIT_CARD_3),
/No matching record\./
);
@ -663,12 +655,18 @@ add_task(async function test_getDuplicateGuid() {
// This number differs from TEST_CREDIT_CARD_3 by swapping the order of the
// 09 and 90 adjacent digits, which is still a valid credit card number.
record["cc-number"] = "358999378390" + last4Digits;
Assert.equal(await profileStorage.creditCards.getDuplicateGuid(record), null);
// We treat numbers with the same last 4 digits as a duplicate.
// ... However, we treat numbers with the same last 4 digits as a duplicate if
// the master password is enabled.
let tokendb = Cc["@mozilla.org/security/pk11tokendb;1"].createInstance(Ci.nsIPK11TokenDB);
let token = tokendb.getInternalKeyToken();
token.reset();
token.initPassword("password");
Assert.equal(await profileStorage.creditCards.getDuplicateGuid(record), guid);
// Even though the last 4 digits are the same, an invalid credit card number
// should never be treated as a duplicate.
// ... Even though the master password is enabled and the last 4 digits are the
// same, an invalid credit card number should never be treated as a duplicate.
record["cc-number"] = "************" + last4Digits;
Assert.equal(await profileStorage.creditCards.getDuplicateGuid(record), null);
});

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

@ -7,10 +7,9 @@
ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
let FormAutofillParent;
let OSKeyStore;
add_task(async function setup() {
({FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {}));
({OSKeyStore} = ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", {}));
ChromeUtils.import("resource://formautofill/MasterPassword.jsm");
});
const TEST_ADDRESS_1 = {
@ -177,24 +176,37 @@ add_task(async function test_getRecords_creditCards() {
let encryptedCCRecords = await Promise.all([TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2].map(async record => {
let clonedRecord = Object.assign({}, record);
clonedRecord["cc-number"] = CreditCard.getLongMaskedNumber(record["cc-number"]);
clonedRecord["cc-number-encrypted"] = await OSKeyStore.encrypt(record["cc-number"]);
clonedRecord["cc-number-encrypted"] = await MasterPassword.encrypt(record["cc-number"]);
return clonedRecord;
}));
sinon.stub(collection, "getAll", () =>
Promise.resolve([Object.assign({}, encryptedCCRecords[0]), Object.assign({}, encryptedCCRecords[1])]));
let CreditCardsWithDecryptedNumber = [
Object.assign({}, encryptedCCRecords[0], {"cc-number-decrypted": TEST_CREDIT_CARD_1["cc-number"]}),
Object.assign({}, encryptedCCRecords[1], {"cc-number-decrypted": TEST_CREDIT_CARD_2["cc-number"]}),
];
let testCases = [
{
description: "If the search string could match multiple creditCards",
description: "If the search string could match 1 creditCard (without masterpassword)",
filter: {
collectionName: "creditCards",
info: {fieldName: "cc-name"},
searchString: "John Doe",
},
expectedResult: CreditCardsWithDecryptedNumber.slice(0, 1),
},
{
description: "If the search string could match multiple creditCards (without masterpassword)",
filter: {
collectionName: "creditCards",
info: {fieldName: "cc-name"},
searchString: "John",
},
expectedResult: encryptedCCRecords,
expectedResult: CreditCardsWithDecryptedNumber,
},
{
description: "If the search string could not match any creditCard",
description: "If the search string could not match any creditCard (without masterpassword)",
filter: {
collectionName: "creditCards",
info: {fieldName: "cc-name"},
@ -203,17 +215,25 @@ add_task(async function test_getRecords_creditCards() {
expectedResult: [],
},
{
description: "Return all creditCards if focused field is cc number; " +
"if the search string could match multiple creditCards",
description: "If the search number string could match 1 creditCard (without masterpassword)",
filter: {
collectionName: "creditCards",
info: {fieldName: "cc-number"},
searchString: "411",
},
expectedResult: CreditCardsWithDecryptedNumber.slice(0, 1),
},
{
description: "If the search string could match multiple creditCards (without masterpassword)",
filter: {
collectionName: "creditCards",
info: {fieldName: "cc-number"},
searchString: "4",
},
expectedResult: encryptedCCRecords,
expectedResult: CreditCardsWithDecryptedNumber,
},
{
description: "If the search string could match 1 creditCard",
description: "If the search string could match 1 creditCard (with masterpassword)",
filter: {
collectionName: "creditCards",
info: {fieldName: "cc-name"},
@ -223,7 +243,7 @@ add_task(async function test_getRecords_creditCards() {
expectedResult: encryptedCCRecords.slice(0, 1),
},
{
description: "Return all creditCards if focused field is cc number",
description: "Return all creditCards if focused field is cc number (with masterpassword)",
filter: {
collectionName: "creditCards",
info: {fieldName: "cc-number"},

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

@ -0,0 +1,119 @@
/**
* Tests of MasterPassword.jsm
*/
"use strict";
const {MockRegistrar} =
ChromeUtils.import("resource://testing-common/MockRegistrar.jsm", {});
let MasterPassword;
add_task(async function setup() {
({MasterPassword} = ChromeUtils.import("resource://formautofill/MasterPassword.jsm", {}));
});
const TESTCASES = [{
description: "With master password set",
masterPassword: "fakemp",
mpEnabled: true,
},
{
description: "Without master password set",
masterPassword: "", // "" means no master password
mpEnabled: false,
}];
// Tests that PSM can successfully ask for a password from the user and relay it
// back to NSS. Does so by mocking out the actual dialog and "filling in" the
// password. Also tests that providing an incorrect password will fail (well,
// technically the user will just get prompted again, but if they then cancel
// the dialog the overall operation will fail).
let gMockPrompter = {
passwordToTry: null,
numPrompts: 0,
// This intentionally does not use arrow function syntax to avoid an issue
// where in the context of the arrow function, |this != gMockPrompter| due to
// how objects get wrapped when going across xpcom boundaries.
promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
this.numPrompts++;
if (this.numPrompts > 1) { // don't keep retrying a bad password
return false;
}
equal(text,
"Please enter your master password.",
"password prompt text should be as expected");
equal(checkMsg, null, "checkMsg should be null");
ok(this.passwordToTry, "passwordToTry should be non-null");
password.value = this.passwordToTry;
return true;
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
};
// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
// to call promptPassword. We return the mock one, above.
let gWindowWatcher = {
getNewPrompter: () => gMockPrompter,
QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowWatcher]),
};
// Ensure that the appropriate initialization has happened.
do_get_profile();
let windowWatcherCID =
MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
gWindowWatcher);
registerCleanupFunction(() => {
MockRegistrar.unregister(windowWatcherCID);
});
TESTCASES.forEach(testcase => {
add_task(async function test_encrypt_decrypt() {
info("Starting testcase: " + testcase.description);
let token = MasterPassword._token;
token.initPassword(testcase.masterPassword);
// Test only: Force the token login without asking for master password
token.login(/* force */ false);
Assert.equal(testcase.mpEnabled, token.isLoggedIn(), "Token should now be logged into");
Assert.equal(MasterPassword.isEnabled, testcase.mpEnabled);
let testText = "test string";
let cipherText = await MasterPassword.encrypt(testText);
Assert.notEqual(testText, cipherText);
let plainText = await MasterPassword.decrypt(cipherText);
Assert.equal(testText, plainText);
if (token.isLoggedIn()) {
// Reset state.
gMockPrompter.numPrompts = 0;
token.logoutSimple();
ok(!token.isLoggedIn(),
"Token should be logged out after calling logoutSimple()");
// Try with the correct password.
gMockPrompter.passwordToTry = testcase.masterPassword;
await MasterPassword.encrypt(testText);
Assert.equal(gMockPrompter.numPrompts, 1, "should have prompted for encryption");
// Reset state.
gMockPrompter.numPrompts = 0;
token.logoutSimple();
try {
// Try with the incorrect password.
gMockPrompter.passwordToTry = "XXX";
await MasterPassword.decrypt(cipherText);
throw new Error("Not receiving canceled master password error");
} catch (e) {
Assert.equal(e.message, "User canceled master password entry");
}
}
token.reset();
});
});

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

@ -4,19 +4,16 @@
"use strict";
ChromeUtils.import("resource://testing-common/LoginTestUtils.jsm", this);
let FormAutofillStorage;
let OSKeyStore;
add_task(async function setup() {
({FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {}));
({OSKeyStore} = ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", {}));
});
const TEST_STORE_FILE_NAME = "test-profile.json";
const ADDRESS_SCHEMA_VERSION = 1;
const CREDIT_CARD_SCHEMA_VERSION = 2;
const CREDIT_CARD_SCHEMA_VERSION = 1;
const ADDRESS_TESTCASES = [
{
@ -249,12 +246,11 @@ add_task(async function test_migrateAddressRecords() {
let profileStorage = new FormAutofillStorage(path);
await profileStorage.initialize();
for (let testcase of ADDRESS_TESTCASES) {
await Promise.all(ADDRESS_TESTCASES.map(async testcase => {
info(testcase.description);
profileStorage._store.data.addresses = [testcase.record];
await profileStorage.addresses._migrateRecord(testcase.record, 0);
do_check_record_matches(testcase.expectedResult, profileStorage.addresses._data[0]);
}
await profileStorage.addresses._migrateRecord(testcase.record);
do_check_record_matches(testcase.expectedResult, testcase.record);
}));
});
add_task(async function test_migrateCreditCardRecords() {
@ -263,79 +259,9 @@ add_task(async function test_migrateCreditCardRecords() {
let profileStorage = new FormAutofillStorage(path);
await profileStorage.initialize();
for (let testcase of CREDIT_CARD_TESTCASES) {
await Promise.all(CREDIT_CARD_TESTCASES.map(async testcase => {
info(testcase.description);
profileStorage._store.data.creditCards = [testcase.record];
await profileStorage.creditCards._migrateRecord(testcase.record, 0);
do_check_record_matches(testcase.expectedResult, profileStorage.creditCards._data[0]);
}
await profileStorage.creditCards._migrateRecord(testcase.record);
do_check_record_matches(testcase.expectedResult, testcase.record);
}));
});
add_task(async function test_migrateEncryptedCreditCardNumber() {
let path = getTempFile(TEST_STORE_FILE_NAME).path;
let profileStorage = new FormAutofillStorage(path);
await profileStorage.initialize();
const ccNumber = "4111111111111111";
let cryptoSDR = Cc["@mozilla.org/login-manager/crypto/SDR;1"]
.createInstance(Ci.nsILoginManagerCrypto);
info("Encrypted credit card should be migrated from v1 to v2");
let record = {
guid: "test-guid",
version: 1,
"cc-name": "Timothy",
"cc-number-encrypted": cryptoSDR.encrypt(ccNumber),
};
let expectedRecord = {
guid: "test-guid",
version: CREDIT_CARD_SCHEMA_VERSION,
"cc-name": "Timothy",
"cc-given-name": "Timothy",
};
profileStorage._store.data.creditCards = [record];
await profileStorage.creditCards._migrateRecord(record, 0);
record = profileStorage.creditCards._data[0];
Assert.equal(expectedRecord.guid, record.guid);
Assert.equal(expectedRecord.version, record.version);
Assert.equal(expectedRecord["cc-name"], record["cc-name"]);
Assert.equal(expectedRecord["cc-given-name"], record["cc-given-name"]);
// Ciphertext of OS Key Store is not stable, must compare decrypted text here.
Assert.equal(ccNumber, await OSKeyStore.decrypt(record["cc-number-encrypted"]));
});
add_task(async function test_migrateEncryptedCreditCardNumberWithMP() {
LoginTestUtils.masterPassword.enable();
let path = getTempFile(TEST_STORE_FILE_NAME).path;
let profileStorage = new FormAutofillStorage(path);
await profileStorage.initialize();
info("Encrypted credit card should be migrated a tombstone if MP is enabled");
let record = {
guid: "test-guid",
version: 1,
"cc-name": "Timothy",
"cc-number-encrypted": "(encrypted to be discarded)",
};
profileStorage._store.data.creditCards = [record];
await profileStorage.creditCards._migrateRecord(record, 0);
record = profileStorage.creditCards._data[0];
Assert.equal(record.guid, "test-guid");
Assert.equal(record.deleted, true);
Assert.equal(typeof record.version, "undefined");
Assert.equal(typeof record["cc-name"], "undefined");
Assert.equal(typeof record["cc-number-encrypted"], "undefined");
LoginTestUtils.masterPassword.disable();
});

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

@ -1,144 +0,0 @@
/**
* Tests of OSKeyStore.jsm
*/
"use strict";
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://testing-common/MockRegistrar.jsm");
let OSKeyStore;
add_task(async function setup() {
({OSKeyStore} = ChromeUtils.import("resource://formautofill/OSKeyStore.jsm", {}));
});
// Ensure that the appropriate initialization has happened.
do_get_profile();
// For NSS key store, mocking out the dialog and control it from here.
let gMockPrompter = {
passwordToTry: "hunter2",
resolve: null,
login: undefined,
// This intentionally does not use arrow function syntax to avoid an issue
// where in the context of the arrow function, |this != gMockPrompter| due to
// how objects get wrapped when going across xpcom boundaries.
promptPassword(dialogTitle, text, password, checkMsg, checkValue) {
equal(text,
"Please enter your master password.",
"password prompt text should be as expected");
equal(checkMsg, null, "checkMsg should be null");
if (this.login) {
password.value = this.passwordToTry;
}
this.resolve();
this.resolve = null;
return this.login;
},
QueryInterface: ChromeUtils.generateQI([Ci.nsIPrompt]),
};
// Mock nsIWindowWatcher. PSM calls getNewPrompter on this to get an nsIPrompt
// to call promptPassword. We return the mock one, above.
let gWindowWatcher = {
getNewPrompter: () => gMockPrompter,
QueryInterface: ChromeUtils.generateQI([Ci.nsIWindowWatcher]),
};
let nssToken;
const TEST_ONLY_REAUTH = "extensions.formautofill.osKeyStore.unofficialBuildOnlyLogin";
async function waitForReauth(login = false) {
if (OSKeyStore.isNSSKeyStore) {
gMockPrompter.login = login;
await new Promise(resolve => { gMockPrompter.resolve = resolve; });
return;
}
let value = login ? "pass" : "cancel";
Services.prefs.setStringPref(TEST_ONLY_REAUTH, value);
await TestUtils.topicObserved("oskeystore-testonly-reauth",
(subject, data) => data == value);
}
const testText = "test string";
let cipherText;
add_task(async function test_encrypt_decrypt() {
Assert.equal(await OSKeyStore.ensureLoggedIn(), true, "Started logged in.");
cipherText = await OSKeyStore.encrypt(testText);
Assert.notEqual(testText, cipherText);
let plainText = await OSKeyStore.decrypt(cipherText);
Assert.equal(testText, plainText);
});
// TODO: skipped because re-auth is not implemented (bug 1429265).
add_task(async function test_reauth() {
let canTest = OSKeyStore.isNSSKeyStore || !AppConstants.MOZILLA_OFFICIAL;
if (!canTest) {
todo_check_false(canTest,
"test_reauth: Cannot test OS key store login on official builds.");
return;
}
if (OSKeyStore.isNSSKeyStore) {
let windowWatcherCID;
windowWatcherCID =
MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1",
gWindowWatcher);
registerCleanupFunction(() => {
MockRegistrar.unregister(windowWatcherCID);
});
// If we use the NSS key store implementation test that everything works
// when a master password is set.
// Set an initial password.
let tokenDB = Cc["@mozilla.org/security/pk11tokendb;1"]
.getService(Ci.nsIPK11TokenDB);
nssToken = tokenDB.getInternalKeyToken();
nssToken.initPassword("hunter2");
}
let reauthObserved = waitForReauth(false);
await new Promise(resolve => TestUtils.executeSoon(resolve));
try {
await OSKeyStore.decrypt(cipherText, true);
throw new Error("Not receiving canceled OS unlock error");
} catch (ex) {
Assert.equal(ex.message, "User canceled OS unlock entry");
Assert.equal(ex.result, Cr.NS_ERROR_ABORT);
}
await reauthObserved;
reauthObserved = waitForReauth(false);
await new Promise(resolve => TestUtils.executeSoon(resolve));
Assert.equal(await OSKeyStore.ensureLoggedIn(true), false, "Reauth cancelled.");
await reauthObserved;
reauthObserved = waitForReauth(true);
await new Promise(resolve => TestUtils.executeSoon(resolve));
let plainText2 = await OSKeyStore.decrypt(cipherText, true);
await reauthObserved;
Assert.equal(testText, plainText2);
reauthObserved = waitForReauth(true);
await new Promise(resolve => TestUtils.executeSoon(resolve));
Assert.equal(await OSKeyStore.ensureLoggedIn(true), true, "Reauth logged in.");
await reauthObserved;
}).skip();
add_task(async function test_decryption_failure() {
try {
await OSKeyStore.decrypt("Malformed cipher text");
throw new Error("Not receiving decryption error");
} catch (ex) {
Assert.notEqual(ex.result, Cr.NS_ERROR_ABORT);
}
});

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

@ -470,7 +470,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
parent: {
// So when we last wrote the record to the server, it had these values.
"guid": "2bbd2d8fbc6b",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -485,7 +485,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
// we can deduce the record hasn't actually been changed remotely so we
// can safely ignore the incoming record and write our local changes.
"guid": "2bbd2d8fbc6b",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -499,7 +499,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Remote change",
parent: {
"guid": "e3680e9f890d",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -509,7 +509,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "e3680e9f890d",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4929001587121045",
},
@ -524,7 +524,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "New local field",
parent: {
"guid": "0cba738b1be0",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -535,7 +535,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "0cba738b1be0",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -550,7 +550,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "New remote field",
parent: {
"guid": "be3ef97f8285",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -560,7 +560,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "be3ef97f8285",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -576,7 +576,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Deleted field locally",
parent: {
"guid": "9627322248ec",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -587,7 +587,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "9627322248ec",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -602,7 +602,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Deleted field remotely",
parent: {
"guid": "7d7509f3eeb2",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -614,7 +614,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "7d7509f3eeb2",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -629,7 +629,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
parent: {
// The last time we wrote this to the server, "cc-exp-month" was 12.
"guid": "e087a06dfc57",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -643,7 +643,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
remote: {
// Remotely, we've changed "cc-exp-month" to 1.
"guid": "e087a06dfc57",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 1,
@ -659,7 +659,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Multiple local changes",
parent: {
"guid": "340a078c596f",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -673,7 +673,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "340a078c596f",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-year": 2000,
@ -692,7 +692,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Same change to local and remote",
parent: {
"guid": "0b3a72a1bea2",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -702,7 +702,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "0b3a72a1bea2",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4929001587121045",
},
@ -717,7 +717,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
parent: {
// This is what we last wrote to the sync server.
"guid": "62068784d089",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -729,7 +729,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
remote: {
// An incoming record has a different cc-number than any of the above!
"guid": "62068784d089",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4929001587121045",
},
@ -750,7 +750,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Conflicting changes to multiple fields",
parent: {
"guid": "244dbb692e94",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -762,7 +762,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "244dbb692e94",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4929001587121045",
"cc-exp-month": 3,
@ -783,7 +783,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Field deleted locally, changed remotely",
parent: {
"guid": "6fc45e03d19a",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -794,7 +794,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "6fc45e03d19a",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 3,
@ -814,7 +814,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Field changed locally, deleted remotely",
parent: {
"guid": "fff9fa27fa18",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"cc-exp-month": 12,
@ -826,7 +826,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "fff9fa27fa18",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
},
@ -848,7 +848,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Created, last modified time reconciliation without local changes",
parent: {
"guid": "5113f329c42f",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"timeCreated": 1234,
@ -859,7 +859,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
local: [],
remote: {
"guid": "5113f329c42f",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"timeCreated": 1200,
@ -883,7 +883,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
description: "Created, last modified time reconciliation with local changes",
parent: {
"guid": "791e5608b80a",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"timeCreated": 1234,
@ -897,7 +897,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
}],
remote: {
"guid": "791e5608b80a",
"version": 2,
"version": 1,
"cc-name": "John Doe",
"cc-number": "4111111111111111",
"timeCreated": 1300,
@ -922,7 +922,7 @@ add_task(async function test_reconcile_unknown_version() {
// Cross-version reconciliation isn't supported yet. See bug 1377204.
await Assert.rejects(profileStorage.addresses.reconcile({
"guid": "31d83d2725ec",
"version": 3,
"version": 2,
"given-name": "Mark",
"family-name": "Hammond",
}), /Got unknown record version/);

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

@ -38,10 +38,10 @@ support-files =
[test_isCJKName.js]
[test_isFieldEligibleForAutofill.js]
[test_markAsAutofillField.js]
[test_masterPassword.js]
[test_migrateRecords.js]
[test_nameUtils.js]
[test_onFormSubmitted.js]
[test_osKeyStore.js]
[test_parseAddressFormat.js]
[test_profileAutocompleteResult.js]
[test_phoneNumber.js]

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

@ -15,7 +15,7 @@ interface nsIOSKeyStore: nsISupports {
* Usage:
*
* // obtain the singleton OSKeyStore instance
* const oskeystore = Cc["@mozilla.org/security/oskeystore;1"].getService(Ci.nsIOSKeyStore);
* const oskeystore = Cc["@mozilla.org/oskeystore;1"].getService(Ci.nsIOSKeyStore);
*
* const PASSWORD_LABEL = "mylabel1";
* const COOKIE_LABEL = "mylabel2";

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

@ -14,8 +14,8 @@ ChromeUtils.import("resource://tps/logger.jsm");
ChromeUtils.defineModuleGetter(this, "formAutofillStorage",
"resource://formautofill/FormAutofillStorage.jsm");
ChromeUtils.defineModuleGetter(this, "OSKeyStore",
"resource://formautofill/OSKeyStore.jsm");
ChromeUtils.defineModuleGetter(this, "MasterPassword",
"resource://formautofill/MasterPassword.jsm");
class FormAutofillBase {
constructor(props, subStorageName, fields) {
@ -111,7 +111,7 @@ class CreditCard extends FormAutofillBase {
async Find() {
const storage = await this.getStorage();
await Promise.all(storage._data.map(
async entry => entry["cc-number"] = await OSKeyStore.decrypt(entry["cc-number-encrypted"])));
async entry => entry["cc-number"] = await MasterPassword.decrypt(entry["cc-number-encrypted"])));
return storage._data.find(entry => {
return this._fields.every(field => entry[field] === this.props[field]);
});

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

@ -6,8 +6,8 @@
var EXPORTED_SYMBOLS = ["CreditCard"];
ChromeUtils.defineModuleGetter(this, "OSKeyStore",
"resource://formautofill/OSKeyStore.jsm");
ChromeUtils.defineModuleGetter(this, "MasterPassword",
"resource://formautofill/MasterPassword.jsm");
// The list of known and supported credit card network ids ("types")
// This list mirrors the networks from dom/payments/BasicCardPayment.cpp
@ -208,13 +208,7 @@ class CreditCard {
if (showNumbers) {
if (this._encryptedNumber) {
try {
label = await OSKeyStore.decrypt(this._encryptedNumber);
} catch (ex) {
// Quietly recover from decryption error.
label = this._number;
Cu.reportError(ex);
}
label = await MasterPassword.decrypt(this._encryptedNumber);
} else {
label = this._number;
}

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

@ -4,35 +4,38 @@
"use strict";
ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
ChromeUtils.import("resource://formautofill/OSKeyStore.jsm");
ChromeUtils.import("resource://testing-common/OSKeyStoreTestUtils.jsm");
ChromeUtils.import("resource://formautofill/MasterPassword.jsm");
let oldGetters = {};
let gFakeLoggedIn = true;
add_task(function setup() {
OSKeyStoreTestUtils.setup();
oldGetters.isLoggedIn = Object.getOwnPropertyDescriptor(OSKeyStore, "isLoggedIn").get;
OSKeyStore.__defineGetter__("isLoggedIn", () => gFakeLoggedIn);
registerCleanupFunction(async () => {
OSKeyStore.__defineGetter__("isLoggedIn", oldGetters.isLoggedIn);
await OSKeyStoreTestUtils.cleanup();
oldGetters._token = Object.getOwnPropertyDescriptor(MasterPassword, "_token").get;
oldGetters.isEnabled = Object.getOwnPropertyDescriptor(MasterPassword, "isEnabled").get;
oldGetters.isLoggedIn = Object.getOwnPropertyDescriptor(MasterPassword, "isLoggedIn").get;
MasterPassword.__defineGetter__("_token", () => { return {hasPassword: true}; });
MasterPassword.__defineGetter__("isEnabled", () => true);
MasterPassword.__defineGetter__("isLoggedIn", () => gFakeLoggedIn);
registerCleanupFunction(() => {
MasterPassword.__defineGetter__("_token", oldGetters._token);
MasterPassword.__defineGetter__("isEnabled", oldGetters.isEnabled);
MasterPassword.__defineGetter__("isLoggedIn", oldGetters.isLoggedIn);
// CreditCard.jsm, OSKeyStore.jsm, and OSKeyStoreTestUtils.jsm are imported
// into the global scope -- the window -- above. If they're not deleted,
// they outlive the test and are reported as a leak.
delete window.OSKeyStore;
// CreditCard.jsm and MasterPassword.jsm are imported into the global scope
// -- the window -- above. If they're not deleted, they outlive the test and
// are reported as a leak.
delete window.MasterPassword;
delete window.CreditCard;
delete window.OSKeyStoreTestUtils;
});
});
add_task(async function test_getLabel_withOSKeyStore() {
ok(OSKeyStore.isLoggedIn, "Confirm that OSKeyStore is faked and thinks it is logged in");
add_task(async function test_getLabel_withMasterPassword() {
ok(MasterPassword.isEnabled, "Confirm that MasterPassword is faked and thinks it is enabled");
ok(MasterPassword.isLoggedIn, "Confirm that MasterPassword is faked and thinks it is logged in");
const ccNumber = "4111111111111111";
const encryptedNumber = await OSKeyStore.encrypt(ccNumber);
const decryptedNumber = await OSKeyStore.decrypt(encryptedNumber);
const encryptedNumber = await MasterPassword.encrypt(ccNumber);
const decryptedNumber = await MasterPassword.decrypt(encryptedNumber);
is(decryptedNumber, ccNumber, "Decrypted CC number should match original");
const name = "Foxkeh";