зеркало из https://github.com/mozilla/gecko-dev.git
Bug 641120 - Enhance PKCS #11 module load dialog. r=keeler
This changes does several things: 1. Changes some titles to include the word "driver" for better clarity. 2. Moves and cleans up the JS implementation of load_device.xul. Having a cleaner implementation in a separate file makes the code easier to discover and maintain. 3. Removes code that tries to show a special case message if a module was already loaded. 3A. The backend code doesn't provide distinction from this case and failure to add in general. 3B. The backend code would only return the error code being checked for if a blank module name was provided. 4. Adds tests. MozReview-Commit-ID: 8BxKWKw5rvp --HG-- extra : rebase_source : 15a29bf7d46f523a11eac37c9f0c6efb2b5d0114
This commit is contained in:
Родитель
b607e644b6
Коммит
150742ba74
|
@ -243,7 +243,6 @@ PKCS12UnknownErr=The PKCS #12 operation failed for unknown reasons.
|
|||
PKCS12InfoNoSmartcardBackup=It is not possible to back up certificates from a hardware security device such as a smart card.
|
||||
PKCS12DupData=The certificate and private key already exist on the security device.
|
||||
AddModuleFailure=Unable to add module
|
||||
AddModuleDup=Security Module already exists
|
||||
DelModuleWarning=Are you sure you want to delete this security module?
|
||||
DelModuleError=Unable to delete module
|
||||
AVATemplate=%S = %S
|
||||
|
|
|
@ -33,4 +33,4 @@
|
|||
<!ENTITY loaddevice.browse "Browse…">
|
||||
<!ENTITY loaddevice.browse.accesskey "B">
|
||||
|
||||
<!ENTITY loaddevice.title "Load PKCS#11 Device">
|
||||
<!ENTITY loaddevice.title2 "Load PKCS#11 Device Driver">
|
||||
|
|
|
@ -129,7 +129,7 @@ pw_not_wanted=Warning! You have decided not to use a Master Password.
|
|||
pw_empty_warning=Your stored web and email passwords, form data, and private keys will not be protected.
|
||||
pw_change2empty_in_fips_mode=You are currently in FIPS mode. FIPS requires a non-empty Master Password.
|
||||
login_failed=Failed to Login
|
||||
loadPK11TokenDialog=Choose a PKCS#11 device to load
|
||||
loadPK11ModuleFilePickerTitle=Choose a PKCS#11 device driver to load
|
||||
devinfo_modname=Module
|
||||
devinfo_modpath=Path
|
||||
devinfo_label=Label
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const nsIFilePicker = Components.interfaces.nsIFilePicker;
|
||||
const nsFilePicker = "@mozilla.org/filepicker;1";
|
||||
const nsIPKCS11Slot = Components.interfaces.nsIPKCS11Slot;
|
||||
const nsIPKCS11Module = Components.interfaces.nsIPKCS11Module;
|
||||
const nsPKCS11ModuleDB = "@mozilla.org/security/pkcs11moduledb;1";
|
||||
|
@ -398,37 +396,6 @@ function changePassword() {
|
|||
enableButtons();
|
||||
}
|
||||
|
||||
// browse fs for PKCS#11 device
|
||||
function doBrowseFiles() {
|
||||
var srbundle = document.getElementById("pippki_bundle");
|
||||
var fp = Components.classes[nsFilePicker].createInstance(nsIFilePicker);
|
||||
fp.init(window,
|
||||
srbundle.getString("loadPK11TokenDialog"),
|
||||
nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(nsIFilePicker.filterAll);
|
||||
if (fp.show() == nsIFilePicker.returnOK) {
|
||||
var pathbox = document.getElementById("device_path");
|
||||
pathbox.setAttribute("value", fp.file.path);
|
||||
}
|
||||
}
|
||||
|
||||
function doLoadDevice() {
|
||||
var name_box = document.getElementById("device_name");
|
||||
var path_box = document.getElementById("device_path");
|
||||
try {
|
||||
getPKCS11().addModule(name_box.value, path_box.value, 0, 0);
|
||||
} catch (e) {
|
||||
if (e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) {
|
||||
doPrompt(getNSSString("AddModuleDup"));
|
||||
} else {
|
||||
doPrompt(getNSSString("AddModuleFailure"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------- Old code
|
||||
|
||||
function showTokenInfo() {
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/* 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/. */
|
||||
/* import-globals-from pippki.js */
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @file Implements the functionality of load_device.xul: a dialog that allows
|
||||
* a PKCS #11 module to be loaded into Firefox.
|
||||
*/
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
function onBrowseBtnPress() {
|
||||
let bundle = document.getElementById("pippki_bundle");
|
||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
fp.init(window, bundle.getString("loadPK11ModuleFilePickerTitle"),
|
||||
Ci.nsIFilePicker.modeOpen);
|
||||
fp.appendFilters(Ci.nsIFilePicker.filterAll);
|
||||
fp.open(rv => {
|
||||
if (rv == Ci.nsIFilePicker.returnOK) {
|
||||
document.getElementById("device_path").value = fp.file.path;
|
||||
}
|
||||
|
||||
// This notification gets sent solely for test purposes. It should not be
|
||||
// used by production code.
|
||||
Services.obs.notifyObservers(window, "LoadPKCS11Module:FilePickHandled");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ondialogaccept() handler.
|
||||
*
|
||||
* @returns {Boolean} true to make the dialog close, false otherwise.
|
||||
*/
|
||||
function onDialogAccept() {
|
||||
let bundle = document.getElementById("pipnss_bundle");
|
||||
let nameBox = document.getElementById("device_name");
|
||||
let pathBox = document.getElementById("device_path");
|
||||
let pkcs11 = Cc["@mozilla.org/security/pkcs11;1"].getService(Ci.nsIPKCS11);
|
||||
|
||||
try {
|
||||
pkcs11.addModule(nameBox.value, pathBox.value, 0, 0);
|
||||
} catch (e) {
|
||||
alertPromptService(null, bundle.getString("AddModuleFailure"));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -8,22 +8,22 @@
|
|||
<!DOCTYPE dialog [
|
||||
<!ENTITY % deviceManangerDTD SYSTEM "chrome://pippki/locale/deviceManager.dtd">
|
||||
%deviceManangerDTD;
|
||||
<!ENTITY % pippkiDTD SYSTEM "chrome://pippki/locale/pippki.dtd" >
|
||||
%pippkiDTD;
|
||||
]>
|
||||
|
||||
<dialog id="loaddevice"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&loaddevice.title;"
|
||||
<dialog id="loaddevice"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&loaddevice.title2;"
|
||||
buttons="accept,cancel"
|
||||
ondialogaccept="return doLoadDevice();">
|
||||
ondialogaccept="return onDialogAccept();">
|
||||
|
||||
<stringbundleset id="stringbundleset">
|
||||
<stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
|
||||
<stringbundle id="pipnss_bundle" src="chrome://pipnss/locale/pipnss.properties"/>
|
||||
</stringbundleset>
|
||||
|
||||
<script type="application/javascript" src="chrome://pippki/content/device_manager.js"/>
|
||||
<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://pippki/content/load_device.js"/>
|
||||
|
||||
<description>&loaddevice.info;</description>
|
||||
<hbox align="center">
|
||||
|
@ -35,8 +35,9 @@
|
|||
<label value="&loaddevice.filename;" accesskey="&loaddevice.filename.accesskey;"
|
||||
control="device_path"/>
|
||||
<textbox id="device_path" flex="1"/>
|
||||
<button label="&loaddevice.browse;" flex="1"
|
||||
accesskey="&loaddevice.browse.accesskey;" oncommand="doBrowseFiles();"/>
|
||||
<button id="browse" label="&loaddevice.browse;" flex="1"
|
||||
accesskey="&loaddevice.browse.accesskey;"
|
||||
oncommand="onBrowseBtnPress();"/>
|
||||
</hbox>
|
||||
|
||||
</dialog>
|
||||
|
|
|
@ -32,6 +32,7 @@ pippki.jar:
|
|||
content/pippki/editcacert.xul (content/editcacert.xul)
|
||||
content/pippki/exceptionDialog.js (content/exceptionDialog.js)
|
||||
* content/pippki/exceptionDialog.xul (content/exceptionDialog.xul)
|
||||
content/pippki/load_device.js (content/load_device.js)
|
||||
content/pippki/load_device.xul (content/load_device.xul)
|
||||
content/pippki/pippki.js (content/pippki.js)
|
||||
content/pippki/protectedAuth.js (content/protectedAuth.js)
|
||||
|
|
|
@ -16,3 +16,4 @@ support-files =
|
|||
# failures, almost entirely on Linux. See Bug 1309519.
|
||||
skip-if = os == "linux"
|
||||
[browser_exportP12_passwordUI.js]
|
||||
[browser_loadPKCS11Module_ui.js]
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/publicdomain/zero/1.0/
|
||||
"use strict";
|
||||
|
||||
// Tests the dialog used for loading PKCS #11 modules.
|
||||
|
||||
const { MockRegistrar } =
|
||||
Cu.import("resource://testing-common/MockRegistrar.jsm", {});
|
||||
|
||||
const gMockPKCS11 = {
|
||||
addModuleCallCount: 0,
|
||||
expectedLibPath: "",
|
||||
expectedModuleName: "",
|
||||
throwOnAddModule: false,
|
||||
|
||||
addModule(moduleName, libraryFullPath, cryptoMechanismFlags, cipherFlags) {
|
||||
this.addModuleCallCount++;
|
||||
Assert.equal(moduleName, this.expectedModuleName,
|
||||
"addModule: Name given should be what's in the name textbox");
|
||||
Assert.equal(libraryFullPath, this.expectedLibPath,
|
||||
"addModule: Path given should be what's in the path textbox");
|
||||
Assert.equal(cryptoMechanismFlags, 0,
|
||||
"addModule: No crypto mechanism flags should be passed");
|
||||
Assert.equal(cipherFlags, 0,
|
||||
"addModule: No cipher flags should be passed");
|
||||
|
||||
if (this.throwOnAddModule) {
|
||||
throw new Error(`addModule: Throwing exception`);
|
||||
}
|
||||
},
|
||||
|
||||
deleteModule(moduleName) {
|
||||
Assert.ok(false, `deleteModule: should not be called`);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPKCS11])
|
||||
};
|
||||
|
||||
const gMockPromptService = {
|
||||
alertCallCount: 0,
|
||||
expectedText: "",
|
||||
expectedWindow: null,
|
||||
|
||||
alert(parent, dialogTitle, text) {
|
||||
this.alertCallCount++;
|
||||
Assert.equal(parent, this.expectedWindow,
|
||||
"alert: Parent should be expected window");
|
||||
Assert.equal(dialogTitle, null, "alert: Title should be null");
|
||||
Assert.equal(text, this.expectedText,
|
||||
"alert: Actual and expected text should match");
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptService])
|
||||
};
|
||||
|
||||
var gMockPKCS11CID =
|
||||
MockRegistrar.register("@mozilla.org/security/pkcs11;1",
|
||||
gMockPKCS11);
|
||||
var gMockPromptServiceCID =
|
||||
MockRegistrar.register("@mozilla.org/embedcomp/prompt-service;1",
|
||||
gMockPromptService);
|
||||
|
||||
var gMockFilePicker = SpecialPowers.MockFilePicker;
|
||||
gMockFilePicker.init(window);
|
||||
|
||||
var gTempFile = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("TmpD", Ci.nsIFile);
|
||||
gTempFile.append("browser_loadPKCS11Module_ui-fakeModule");
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
gMockFilePicker.cleanup();
|
||||
MockRegistrar.unregister(gMockPKCS11CID);
|
||||
MockRegistrar.unregister(gMockPromptServiceCID);
|
||||
});
|
||||
|
||||
function resetCallCounts() {
|
||||
gMockPKCS11.addModuleCallCount = 0;
|
||||
gMockPromptService.alertCallCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the dialog shown to load a PKCS #11 module.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* A promise that resolves when the dialog has finished loading, with
|
||||
* the window of the opened dialog.
|
||||
*/
|
||||
function openLoadModuleDialog() {
|
||||
let win = window.openDialog("chrome://pippki/content/load_device.xul", "", "");
|
||||
return new Promise(resolve => {
|
||||
win.addEventListener("load", function() {
|
||||
resolve(win);
|
||||
}, {once: true});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Presses the browse button and simulates interacting with the file picker that
|
||||
* should be triggered.
|
||||
*
|
||||
* @param {window} win
|
||||
* The dialog window.
|
||||
* @param {Boolean} cancel
|
||||
* If true, the file picker is canceled. If false, gTempFile is chosen in
|
||||
* the file picker and the file picker is accepted.
|
||||
*/
|
||||
async function browseToTempFile(win, cancel) {
|
||||
gMockFilePicker.showCallback = () => {
|
||||
gMockFilePicker.setFiles([gTempFile]);
|
||||
|
||||
if (cancel) {
|
||||
info("MockFilePicker returning cancel");
|
||||
return Ci.nsIFilePicker.returnCancel;
|
||||
}
|
||||
|
||||
info("MockFilePicker returning OK");
|
||||
return Ci.nsIFilePicker.returnOK;
|
||||
};
|
||||
|
||||
info("Pressing browse button");
|
||||
win.document.getElementById("browse").doCommand();
|
||||
await TestUtils.topicObserved("LoadPKCS11Module:FilePickHandled");
|
||||
}
|
||||
|
||||
add_task(async function testBrowseButton() {
|
||||
let win = await openLoadModuleDialog();
|
||||
let pathBox = win.document.getElementById("device_path");
|
||||
let originalPathBoxValue = "expected path if picker is canceled";
|
||||
pathBox.value = originalPathBoxValue;
|
||||
|
||||
// Test what happens if the file picker is canceled.
|
||||
await browseToTempFile(win, true);
|
||||
Assert.equal(pathBox.value, originalPathBoxValue,
|
||||
"Path shown should be unchanged due to canceled picker");
|
||||
|
||||
// Test what happens if the file picker is not canceled.
|
||||
await browseToTempFile(win, false);
|
||||
Assert.equal(pathBox.value, gTempFile.path,
|
||||
"Path shown should be same as the one chosen in the file picker");
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
function testAddModuleHelper(win, throwOnAddModule) {
|
||||
resetCallCounts();
|
||||
gMockPKCS11.expectedLibPath = gTempFile.path;
|
||||
gMockPKCS11.expectedModuleName = "test module";
|
||||
gMockPKCS11.throwOnAddModule = throwOnAddModule;
|
||||
|
||||
win.document.getElementById("device_name").value =
|
||||
gMockPKCS11.expectedModuleName;
|
||||
win.document.getElementById("device_path").value =
|
||||
gMockPKCS11.expectedLibPath;
|
||||
|
||||
info("Accepting dialog");
|
||||
win.document.getElementById("loaddevice").acceptDialog();
|
||||
}
|
||||
|
||||
add_task(async function testAddModuleSuccess() {
|
||||
let win = await openLoadModuleDialog();
|
||||
|
||||
testAddModuleHelper(win, false);
|
||||
await BrowserTestUtils.windowClosed(win);
|
||||
|
||||
Assert.equal(gMockPKCS11.addModuleCallCount, 1,
|
||||
"addModule() should have been called once");
|
||||
Assert.equal(gMockPromptService.alertCallCount, 0,
|
||||
"alert() should never have been called");
|
||||
});
|
||||
|
||||
add_task(async function testAddModuleFailure() {
|
||||
let win = await openLoadModuleDialog();
|
||||
gMockPromptService.expectedText = "Unable to add module";
|
||||
gMockPromptService.expectedWindow = win;
|
||||
|
||||
testAddModuleHelper(win, true);
|
||||
// If adding a module fails, the dialog will not close. As such, we have to
|
||||
// close the window ourselves.
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
|
||||
Assert.equal(gMockPKCS11.addModuleCallCount, 1,
|
||||
"addModule() should have been called once");
|
||||
Assert.equal(gMockPromptService.alertCallCount, 1,
|
||||
"alert() should have been called once");
|
||||
});
|
||||
|
||||
add_task(async function testCancel() {
|
||||
let win = await openLoadModuleDialog();
|
||||
resetCallCounts();
|
||||
|
||||
info("Canceling dialog");
|
||||
win.document.getElementById("loaddevice").cancelDialog();
|
||||
|
||||
Assert.equal(gMockPKCS11.addModuleCallCount, 0,
|
||||
"addModule() should never have been called");
|
||||
Assert.equal(gMockPromptService.alertCallCount, 0,
|
||||
"alert() should never have been called");
|
||||
|
||||
await BrowserTestUtils.windowClosed(win);
|
||||
});
|
Загрузка…
Ссылка в новой задаче