// -*- 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.import( "resource://testing-common/MockRegistrar.jsm" ); 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", }, ]; }, 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, 3, "Expected rememberedList to only have one item" ); let labels = win.document .getElementById("rememberedList") .querySelectorAll("label"); Assert.equal( labels.length, 9, "Expected the rememberedList to have three labels" ); let expectedHosts = ["example.com", "example.org", "example.test"]; let hosts = [labels[0].value, labels[3].value, labels[6].value]; let expectedNames = [cert.commonName, cert2.commonName, cert3.commonName]; let names = [labels[1].value, labels[4].value, labels[7].value]; let expectedSerialNumbers = [ cert.serialNumber, cert2.serialNumber, cert3.serialNumber, ]; let serialNumbers = [labels[2].value, labels[5].value, labels[8].value]; for (let i = 0; i < 3; i++) { Assert.equal(hosts[i], expectedHosts[i], "Expected host to be asciiHost"); Assert.equal( names[i], expectedNames[i], "Expected name to be the commonName of the cert" ); Assert.equal( serialNumbers[i], expectedSerialNumbers[i], "Expected serialNumber to be the serialNumber of the cert" ); } 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); });