summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js
diff options
context:
space:
mode:
Diffstat (limited to 'security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js')
-rw-r--r--security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js297
1 files changed, 297 insertions, 0 deletions
diff --git a/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js b/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js
new file mode 100644
index 0000000000..9f9ce6b28a
--- /dev/null
+++ b/security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js
@@ -0,0 +1,297 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+/**
+ * Test certificate (i.e. build/pgo/certs/mochitest.client).
+ *
+ * @type {nsIX509Cert}
+ */
+var cert;
+var cert2;
+var cert3;
+
+var sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);
+var certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
+ Ci.nsIX509CertDB
+);
+
+var deleted = false;
+
+const { MockRegistrar } = ChromeUtils.importESModule(
+ "resource://testing-common/MockRegistrar.sys.mjs"
+);
+
+function findCertByCommonName(commonName) {
+ for (let cert of certDB.getCerts()) {
+ if (cert.commonName == commonName) {
+ return cert;
+ }
+ }
+ return null;
+}
+
+async function testHelper(connectURL, expectedURL) {
+ let win = await BrowserTestUtils.openNewBrowserWindow();
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.default_personal_cert", "Ask Every Time"]],
+ });
+
+ BrowserTestUtils.loadURI(win.gBrowser.selectedBrowser, connectURL);
+
+ await BrowserTestUtils.browserLoaded(
+ win.gBrowser.selectedBrowser,
+ false,
+ expectedURL,
+ true
+ );
+ let loadedURL = win.gBrowser.selectedBrowser.documentURI.spec;
+ Assert.ok(
+ loadedURL.startsWith(expectedURL),
+ `Expected and actual URLs should match (got '${loadedURL}', expected '${expectedURL}')`
+ );
+
+ await win.close();
+
+ // This clears the TLS session cache so we don't use a previously-established
+ // ticket to connect and bypass selecting a client auth certificate in
+ // subsequent tests.
+ sdr.logout();
+}
+
+async function openRequireClientCert() {
+ gClientAuthDialogs.chooseCertificateCalled = false;
+ await testHelper(
+ "https://requireclientcert.example.com:443",
+ "https://requireclientcert.example.com/"
+ );
+}
+
+async function openRequireClientCert2() {
+ gClientAuthDialogs.chooseCertificateCalled = false;
+ await testHelper(
+ "https://requireclientcert-2.example.com:443",
+ "https://requireclientcert-2.example.com/"
+ );
+}
+
+// Mock implementation of nsIClientAuthRememberService
+const gClientAuthRememberService = {
+ forgetRememberedDecision(key) {
+ deleted = true;
+ Assert.equal(
+ key,
+ "exampleKey2",
+ "Expected to get the same key that was passed in getDecisions()"
+ );
+ },
+
+ getDecisions() {
+ return [
+ {
+ asciiHost: "example.com",
+ dbKey: cert.dbKey,
+ entryKey: "exampleKey1",
+ },
+ {
+ asciiHost: "example.org",
+ dbKey: cert2.dbKey,
+ entryKey: "exampleKey2",
+ },
+ {
+ asciiHost: "example.test",
+ dbKey: cert3.dbKey,
+ entryKey: "exampleKey3",
+ },
+ {
+ asciiHost: "unavailable.example.com",
+ // This dbKey should not correspond to any real certificate. The first
+ // 8 bytes have to be 0, followed by the lengths of the serial number
+ // and issuer distinguished name, respectively, and then followed by
+ // the bytes of the serial number and finally the encoded issuer
+ // distinguished name. In this case, the serial number is a single 0
+ // byte and the issuer distinguished name is a DER SEQUENCE of length 0
+ // (the bytes 0x30 and 0).
+ // See also the documentation in nsNSSCertificateDB::FindCertByDBKey.
+ dbKey: "AAAAAAAAAAAAAAABAAAAAgAeAA==",
+ entryKey: "exampleKey4",
+ },
+ ];
+ },
+
+ QueryInterface: ChromeUtils.generateQI(["nsIClientAuthRememberService"]),
+};
+
+const gClientAuthDialogs = {
+ _chooseCertificateCalled: false,
+
+ get chooseCertificateCalled() {
+ return this._chooseCertificateCalled;
+ },
+
+ set chooseCertificateCalled(value) {
+ this._chooseCertificateCalled = value;
+ },
+
+ chooseCertificate(
+ hostname,
+ port,
+ organization,
+ issuerOrg,
+ certList,
+ selectedIndex,
+ rememberClientAuthCertificate
+ ) {
+ rememberClientAuthCertificate.value = true;
+ this.chooseCertificateCalled = true;
+ selectedIndex.value = 0;
+ return true;
+ },
+
+ QueryInterface: ChromeUtils.generateQI([Ci.nsIClientAuthDialogs]),
+};
+
+add_task(async function testRememberedDecisionsUI() {
+ cert = findCertByCommonName("Mochitest client");
+ cert2 = await readCertificate("pgo-ca-all-usages.pem", ",,");
+ cert3 = await readCertificate("client-cert-via-intermediate.pem", ",,");
+ isnot(cert, null, "Should be able to find the test client cert");
+ isnot(cert2, null, "Should be able to find pgo-ca-all-usages.pem");
+ isnot(cert3, null, "Should be able to find client-cert-via-intermediate.pem");
+
+ let clientAuthRememberServiceCID = MockRegistrar.register(
+ "@mozilla.org/security/clientAuthRememberService;1",
+ gClientAuthRememberService
+ );
+
+ let win = await openCertManager();
+
+ let listItems = win.document
+ .getElementById("rememberedList")
+ .querySelectorAll("richlistitem");
+
+ Assert.equal(
+ listItems.length,
+ 4,
+ "rememberedList has expected number of items"
+ );
+
+ let labels = win.document
+ .getElementById("rememberedList")
+ .querySelectorAll("label");
+
+ Assert.equal(
+ labels.length,
+ 12,
+ "rememberedList has expected number of labels"
+ );
+
+ await BrowserTestUtils.waitForCondition(
+ () => !!labels[10].textContent.length,
+ "Localized label is populated"
+ );
+
+ let expectedHosts = [
+ "example.com",
+ "example.org",
+ "example.test",
+ "unavailable.example.com",
+ ];
+ let hosts = [
+ labels[0].value,
+ labels[3].value,
+ labels[6].value,
+ labels[9].value,
+ ];
+ let expectedNames = [
+ cert.commonName,
+ cert2.commonName,
+ cert3.commonName,
+ "(Unavailable)",
+ ];
+ let names = [
+ labels[1].value,
+ labels[4].value,
+ labels[7].value,
+ labels[10].textContent,
+ ];
+ let expectedSerialNumbers = [
+ cert.serialNumber,
+ cert2.serialNumber,
+ cert3.serialNumber,
+ "(Unavailable)",
+ ];
+ let serialNumbers = [
+ labels[2].value,
+ labels[5].value,
+ labels[8].value,
+ labels[11].textContent,
+ ];
+
+ for (let i = 0; i < listItems.length; i++) {
+ Assert.equal(hosts[i], expectedHosts[i], "got expected asciiHost");
+ Assert.equal(names[i], expectedNames[i], "got expected commonName");
+ Assert.equal(
+ serialNumbers[i],
+ expectedSerialNumbers[i],
+ "got expected serialNumber"
+ );
+ }
+
+ win.document.getElementById("rememberedList").selectedIndex = 1;
+ win.document.getElementById("remembered_deleteButton").click();
+
+ Assert.ok(deleted, "Expected forgetRememberedDecision() to get called");
+
+ win.document.getElementById("certmanager").acceptDialog();
+ await BrowserTestUtils.windowClosed(win);
+
+ MockRegistrar.unregister(clientAuthRememberServiceCID);
+});
+
+add_task(async function testDeletingRememberedDecisions() {
+ let clientAuthDialogsCID = MockRegistrar.register(
+ "@mozilla.org/nsClientAuthDialogs;1",
+ gClientAuthDialogs
+ );
+ let cars = Cc["@mozilla.org/security/clientAuthRememberService;1"].getService(
+ Ci.nsIClientAuthRememberService
+ );
+
+ await openRequireClientCert();
+ Assert.ok(
+ gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should have been called if visiting 'requireclientcert.example.com' for the first time"
+ );
+
+ await openRequireClientCert();
+ Assert.ok(
+ !gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should not have been called if visiting 'requireclientcert.example.com' for the second time"
+ );
+
+ await openRequireClientCert2();
+ Assert.ok(
+ gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should have been called if visiting 'requireclientcert-2.example.com' for the first time"
+ );
+
+ let originAttributes = { privateBrowsingId: 0 };
+ cars.deleteDecisionsByHost("requireclientcert.example.com", originAttributes);
+
+ await openRequireClientCert();
+ Assert.ok(
+ gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should have been called after removing all remembered decisions for 'requireclientcert.example.com'"
+ );
+
+ await openRequireClientCert2();
+ Assert.ok(
+ !gClientAuthDialogs.chooseCertificateCalled,
+ "chooseCertificate should not have been called if visiting 'requireclientcert-2.example.com' for the second time"
+ );
+
+ MockRegistrar.unregister(clientAuthDialogsCID);
+});