diff options
Diffstat (limited to 'security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js')
-rw-r--r-- | security/manager/ssl/tests/mochitest/browser/browser_clientAuthRememberService.js | 297 |
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); +}); |