summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/tests/mochitest/browser/browser_clientAuth_connection.js
blob: 6362fd34de956baec00c3e03399a3120800c05e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// -*- 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";

// Tests various scenarios connecting to a server that requires client cert
// authentication. Also tests that nsIClientAuthDialogs.chooseCertificate
// is called at the appropriate times and with the correct arguments.

const { MockRegistrar } =
  Cu.import("resource://testing-common/MockRegistrar.jsm", {});

const DialogState = {
  // Assert that chooseCertificate() is never called.
  ASSERT_NOT_CALLED: "ASSERT_NOT_CALLED",
  // Return that the user selected the first given cert.
  RETURN_CERT_SELECTED: "RETURN_CERT_SELECTED",
  // Return that the user canceled.
  RETURN_CERT_NOT_SELECTED: "RETURN_CERT_NOT_SELECTED",
};

let sdr = Cc["@mozilla.org/security/sdr;1"].getService(Ci.nsISecretDecoderRing);

// Mock implementation of nsIClientAuthDialogs.
const gClientAuthDialogs = {
  _state: DialogState.ASSERT_NOT_CALLED,

  set state(newState) {
    info(`old state: ${this._state}`);
    this._state = newState;
    info(`new state: ${this._state}`);
  },

  get state() {
    return this._state;
  },

  chooseCertificate(ctx, hostname, port, organization, issuerOrg, certList,
                    selectedIndex) {
    Assert.notEqual(this.state, DialogState.ASSERT_NOT_CALLED,
                    "chooseCertificate() should be called only when expected");

    let caud = ctx.QueryInterface(Ci.nsIClientAuthUserDecision);
    Assert.notEqual(caud, null,
                    "nsIClientAuthUserDecision should be queryable from the " +
                    "given context");
    caud.rememberClientAuthCertificate = false;

    Assert.equal(hostname, "requireclientcert.example.com",
                 "Hostname should be 'requireclientcert.example.com'");
    Assert.equal(port, 443, "Port should be 443");
    Assert.equal(organization, "",
                 "Server cert Organization should be empty/not present");
    Assert.equal(issuerOrg, "Mozilla Testing",
                 "Server cert issuer Organization should be 'Mozilla Testing'");

    // For mochitests, only the cert at build/pgo/certs/mochitest.client should
    // be selectable, so we do some brief checks to confirm this.
    Assert.notEqual(certList, null, "Cert list should not be null");
    Assert.equal(certList.length, 1, "Only 1 certificate should be available");
    let cert = certList.queryElementAt(0, Ci.nsIX509Cert);
    Assert.notEqual(cert, null, "Cert list should contain an nsIX509Cert");
    Assert.equal(cert.commonName, "Mochitest client",
                 "Cert CN should be 'Mochitest client'");

    if (this.state == DialogState.RETURN_CERT_SELECTED) {
      selectedIndex.value = 0;
      return true;
    }
    return false;
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIClientAuthDialogs])
};

add_task(function* setup() {
  let clientAuthDialogsCID =
    MockRegistrar.register("@mozilla.org/nsClientAuthDialogs;1",
                           gClientAuthDialogs);
  registerCleanupFunction(() => {
    MockRegistrar.unregister(clientAuthDialogsCID);
  });
});

/**
 * Test helper for the tests below.
 *
 * @param {String} prefValue
 *        Value to set the "security.default_personal_cert" pref to.
 * @param {String} expectedURL
 *        If the connection is expected to load successfully, the URL that
 *        should load. If the connection is expected to fail and result in an
 *        error page, |undefined|.
 */
function* testHelper(prefValue, expectedURL) {
  yield SpecialPowers.pushPrefEnv({"set": [
    ["security.default_personal_cert", prefValue],
  ]});

  yield BrowserTestUtils.loadURI(gBrowser.selectedBrowser,
                                 "https://requireclientcert.example.com:443");

  // |loadedURL| will be a string URL if browserLoaded() wins the race, or
  // |undefined| if waitForErrorPage() wins the race.
  let loadedURL = yield Promise.race([
    BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser),
    BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser),
  ]);
  Assert.equal(expectedURL, loadedURL, "Expected and actual URLs should match");

  // Ensure previously successful connections don't influence future tests.
  sdr.logoutAndTeardown();
}

// Test that if a certificate is chosen automatically the connection succeeds,
// and that nsIClientAuthDialogs.chooseCertificate() is never called.
add_task(function* testCertChosenAutomatically() {
  gClientAuthDialogs.state = DialogState.ASSERT_NOT_CALLED;
  yield* testHelper("Select Automatically",
                    "https://requireclientcert.example.com/");
});

// Test that if the user doesn't choose a certificate, the connection fails and
// an error page is displayed.
add_task(function* testCertNotChosenByUser() {
  gClientAuthDialogs.state = DialogState.RETURN_CERT_NOT_SELECTED;
  yield* testHelper("Ask Every Time", undefined);
});

// Test that if the user chooses a certificate the connection suceeeds.
add_task(function* testCertChosenByUser() {
  gClientAuthDialogs.state = DialogState.RETURN_CERT_SELECTED;
  yield* testHelper("Ask Every Time",
                    "https://requireclientcert.example.com/");
});