1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "base/files/file_path.h"
7 #include "base/synchronization/waitable_event.h"
8 #include "chrome/browser/profiles/profile.h"
9 #include "chrome/browser/ssl/ssl_client_auth_requestor_mock.h"
10 #include "chrome/browser/ui/browser.h"
11 #include "chrome/browser/ui/tabs/tab_strip_model.h"
12 #include "chrome/browser/ui/views/ssl_client_certificate_selector.h"
13 #include "chrome/test/base/in_process_browser_test.h"
14 #include "chrome/test/base/interactive_test_utils.h"
15 #include "chrome/test/base/ui_test_utils.h"
16 #include "content/public/browser/client_certificate_delegate.h"
17 #include "content/public/browser/web_contents.h"
18 #include "content/public/test/browser_test_utils.h"
19 #include "net/base/request_priority.h"
20 #include "net/base/test_data_directory.h"
21 #include "net/cert/x509_certificate.h"
22 #include "net/http/http_transaction_factory.h"
23 #include "net/ssl/ssl_cert_request_info.h"
24 #include "net/test/cert_test_util.h"
25 #include "net/url_request/url_request.h"
26 #include "net/url_request/url_request_context.h"
27 #include "net/url_request/url_request_context_getter.h"
28 #include "testing/gtest/include/gtest/gtest.h"
31 #include "crypto/scoped_test_nss_db.h"
34 using ::testing::Mock
;
35 using ::testing::StrictMock
;
36 using content::BrowserThread
;
38 // We don't have a way to do end-to-end SSL client auth testing, so this test
39 // creates a certificate selector_ manually with a mocked
40 // SSLClientAuthHandler.
42 class SSLClientCertificateSelectorTest
: public InProcessBrowserTest
{
44 SSLClientCertificateSelectorTest()
45 : io_loop_finished_event_(false, false),
50 void SetUpInProcessBrowserTestFixture() override
{
51 base::FilePath certs_dir
= net::GetTestCertsDirectory();
54 // If USE_NSS, the selector tries to unlock the slot where the private key
55 // of each certificate is stored. If no private key is found, the slot would
56 // be null and the unlock will crash.
57 ASSERT_TRUE(test_nssdb_
.is_open());
58 client_cert_1_
= net::ImportClientCertAndKeyFromFile(
59 certs_dir
, "client_1.pem", "client_1.pk8", test_nssdb_
.slot());
60 client_cert_2_
= net::ImportClientCertAndKeyFromFile(
61 certs_dir
, "client_2.pem", "client_2.pk8", test_nssdb_
.slot());
63 // No unlock is attempted if !USE_NSS. Thus, there is no need to import a
65 client_cert_1_
= net::ImportCertFromFile(certs_dir
, "client_1.pem");
66 client_cert_2_
= net::ImportCertFromFile(certs_dir
, "client_2.pem");
68 ASSERT_NE(nullptr, client_cert_1_
.get());
69 ASSERT_NE(nullptr, client_cert_2_
.get());
71 cert_request_info_
= new net::SSLCertRequestInfo
;
72 cert_request_info_
->host_and_port
= net::HostPortPair("foo", 123);
73 cert_request_info_
->client_certs
.push_back(client_cert_1_
);
74 cert_request_info_
->client_certs
.push_back(client_cert_2_
);
77 void SetUpOnMainThread() override
{
78 url_request_context_getter_
= browser()->profile()->GetRequestContext();
80 BrowserThread::PostTask(
81 BrowserThread::IO
, FROM_HERE
,
82 base::Bind(&SSLClientCertificateSelectorTest::SetUpOnIOThread
, this));
84 io_loop_finished_event_
.Wait();
86 content::WaitForLoadStop(
87 browser()->tab_strip_model()->GetActiveWebContents());
88 selector_
= new SSLClientCertificateSelector(
89 browser()->tab_strip_model()->GetActiveWebContents(),
90 auth_requestor_
->cert_request_info_
, auth_requestor_
->CreateDelegate());
94 EXPECT_EQ(client_cert_1_
.get(), selector_
->GetSelectedCert());
97 virtual void SetUpOnIOThread() {
98 url_request_
= MakeURLRequest(url_request_context_getter_
.get()).release();
100 auth_requestor_
= new StrictMock
<SSLClientAuthRequestorMock
>(
104 io_loop_finished_event_
.Signal();
107 // Have to release our reference to the auth handler during the test to allow
108 // it to be destroyed while the Browser and its IO thread still exist.
109 void TearDownOnMainThread() override
{
110 BrowserThread::PostTask(
111 BrowserThread::IO
, FROM_HERE
,
112 base::Bind(&SSLClientCertificateSelectorTest::CleanUpOnIOThread
, this));
114 io_loop_finished_event_
.Wait();
116 auth_requestor_
= NULL
;
119 virtual void CleanUpOnIOThread() {
122 io_loop_finished_event_
.Signal();
126 scoped_ptr
<net::URLRequest
> MakeURLRequest(
127 net::URLRequestContextGetter
* context_getter
) {
128 return context_getter
->GetURLRequestContext()->CreateRequest(
129 GURL("https://example"), net::DEFAULT_PRIORITY
, NULL
);
132 base::WaitableEvent io_loop_finished_event_
;
134 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter_
;
135 net::URLRequest
* url_request_
;
137 scoped_refptr
<net::X509Certificate
> client_cert_1_
;
138 scoped_refptr
<net::X509Certificate
> client_cert_2_
;
139 scoped_refptr
<net::SSLCertRequestInfo
> cert_request_info_
;
140 scoped_refptr
<StrictMock
<SSLClientAuthRequestorMock
> > auth_requestor_
;
141 // The selector will be deleted when a cert is selected or the tab is closed.
142 SSLClientCertificateSelector
* selector_
;
144 crypto::ScopedTestNSSDB test_nssdb_
;
148 class SSLClientCertificateSelectorMultiTabTest
149 : public SSLClientCertificateSelectorTest
{
151 void SetUpInProcessBrowserTestFixture() override
{
152 SSLClientCertificateSelectorTest::SetUpInProcessBrowserTestFixture();
154 cert_request_info_1_
= new net::SSLCertRequestInfo
;
155 cert_request_info_1_
->host_and_port
= net::HostPortPair("bar", 123);
156 cert_request_info_1_
->client_certs
.push_back(client_cert_1_
);
157 cert_request_info_1_
->client_certs
.push_back(client_cert_2_
);
159 cert_request_info_2_
= new net::SSLCertRequestInfo
;
160 cert_request_info_2_
->host_and_port
= net::HostPortPair("bar", 123);
161 cert_request_info_2_
->client_certs
.push_back(client_cert_1_
);
162 cert_request_info_2_
->client_certs
.push_back(client_cert_2_
);
165 void SetUpOnMainThread() override
{
166 // Also calls SetUpOnIOThread.
167 SSLClientCertificateSelectorTest::SetUpOnMainThread();
169 AddTabAtIndex(1, GURL("about:blank"), ui::PAGE_TRANSITION_LINK
);
170 AddTabAtIndex(2, GURL("about:blank"), ui::PAGE_TRANSITION_LINK
);
171 ASSERT_TRUE(NULL
!= browser()->tab_strip_model()->GetWebContentsAt(0));
172 ASSERT_TRUE(NULL
!= browser()->tab_strip_model()->GetWebContentsAt(1));
173 ASSERT_TRUE(NULL
!= browser()->tab_strip_model()->GetWebContentsAt(2));
174 content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(1));
175 content::WaitForLoadStop(browser()->tab_strip_model()->GetWebContentsAt(2));
177 selector_1_
= new SSLClientCertificateSelector(
178 browser()->tab_strip_model()->GetWebContentsAt(1),
179 auth_requestor_1_
->cert_request_info_
,
180 auth_requestor_1_
->CreateDelegate());
183 selector_2_
= new SSLClientCertificateSelector(
184 browser()->tab_strip_model()->GetWebContentsAt(2),
185 auth_requestor_2_
->cert_request_info_
,
186 auth_requestor_2_
->CreateDelegate());
190 EXPECT_EQ(2, browser()->tab_strip_model()->active_index());
191 EXPECT_EQ(client_cert_1_
.get(), selector_1_
->GetSelectedCert());
192 EXPECT_EQ(client_cert_1_
.get(), selector_2_
->GetSelectedCert());
195 void SetUpOnIOThread() override
{
197 MakeURLRequest(url_request_context_getter_
.get()).release();
199 MakeURLRequest(url_request_context_getter_
.get()).release();
201 auth_requestor_1_
= new StrictMock
<SSLClientAuthRequestorMock
>(
203 cert_request_info_1_
);
204 auth_requestor_2_
= new StrictMock
<SSLClientAuthRequestorMock
>(
206 cert_request_info_2_
);
208 SSLClientCertificateSelectorTest::SetUpOnIOThread();
211 void TearDownOnMainThread() override
{
212 auth_requestor_2_
= NULL
;
213 auth_requestor_1_
= NULL
;
214 SSLClientCertificateSelectorTest::TearDownOnMainThread();
217 void CleanUpOnIOThread() override
{
218 delete url_request_1_
;
219 delete url_request_2_
;
220 SSLClientCertificateSelectorTest::CleanUpOnIOThread();
224 net::URLRequest
* url_request_1_
;
225 net::URLRequest
* url_request_2_
;
226 scoped_refptr
<net::SSLCertRequestInfo
> cert_request_info_1_
;
227 scoped_refptr
<net::SSLCertRequestInfo
> cert_request_info_2_
;
228 scoped_refptr
<StrictMock
<SSLClientAuthRequestorMock
> > auth_requestor_1_
;
229 scoped_refptr
<StrictMock
<SSLClientAuthRequestorMock
> > auth_requestor_2_
;
230 SSLClientCertificateSelector
* selector_1_
;
231 SSLClientCertificateSelector
* selector_2_
;
234 class SSLClientCertificateSelectorMultiProfileTest
235 : public SSLClientCertificateSelectorTest
{
237 void SetUpInProcessBrowserTestFixture() override
{
238 SSLClientCertificateSelectorTest::SetUpInProcessBrowserTestFixture();
240 cert_request_info_1_
= new net::SSLCertRequestInfo
;
241 cert_request_info_1_
->host_and_port
= net::HostPortPair("foo", 123);
242 cert_request_info_1_
->client_certs
.push_back(client_cert_1_
);
243 cert_request_info_1_
->client_certs
.push_back(client_cert_2_
);
246 void SetUpOnMainThread() override
{
247 browser_1_
= CreateIncognitoBrowser();
248 url_request_context_getter_1_
= browser_1_
->profile()->GetRequestContext();
250 // Also calls SetUpOnIOThread.
251 SSLClientCertificateSelectorTest::SetUpOnMainThread();
253 selector_1_
= new SSLClientCertificateSelector(
254 browser_1_
->tab_strip_model()->GetActiveWebContents(),
255 auth_requestor_1_
->cert_request_info_
,
256 auth_requestor_1_
->CreateDelegate());
260 EXPECT_EQ(client_cert_1_
.get(), selector_1_
->GetSelectedCert());
263 void SetUpOnIOThread() override
{
265 MakeURLRequest(url_request_context_getter_1_
.get()).release();
267 auth_requestor_1_
= new StrictMock
<SSLClientAuthRequestorMock
>(
269 cert_request_info_1_
);
271 SSLClientCertificateSelectorTest::SetUpOnIOThread();
274 void TearDownOnMainThread() override
{
275 auth_requestor_1_
= NULL
;
276 SSLClientCertificateSelectorTest::TearDownOnMainThread();
279 void CleanUpOnIOThread() override
{
280 delete url_request_1_
;
281 SSLClientCertificateSelectorTest::CleanUpOnIOThread();
286 scoped_refptr
<net::URLRequestContextGetter
> url_request_context_getter_1_
;
287 net::URLRequest
* url_request_1_
;
288 scoped_refptr
<net::SSLCertRequestInfo
> cert_request_info_1_
;
289 scoped_refptr
<StrictMock
<SSLClientAuthRequestorMock
> > auth_requestor_1_
;
290 SSLClientCertificateSelector
* selector_1_
;
293 #if defined(OS_LINUX) && !defined(OS_CHROMEOS) && defined(USE_AURA)
294 // TODO(erg): linux_aura bringup: http://crbug.com/163931
295 #define MAYBE_SelectNone DISABLED_SelectNone
297 #define MAYBE_SelectNone SelectNone
301 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest
, MAYBE_SelectNone
) {
302 EXPECT_CALL(*auth_requestor_
.get(), CancelCertificateSelection());
304 // Let the mock get checked on destruction.
307 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest
, Escape
) {
308 EXPECT_CALL(*auth_requestor_
.get(), CertificateSelected(NULL
));
310 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
311 browser(), ui::VKEY_ESCAPE
, false, false, false, false));
313 Mock::VerifyAndClear(auth_requestor_
.get());
316 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest
, SelectDefault
) {
317 EXPECT_CALL(*auth_requestor_
.get(),
318 CertificateSelected(client_cert_1_
.get()));
320 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
321 browser(), ui::VKEY_RETURN
, false, false, false, false));
323 Mock::VerifyAndClear(auth_requestor_
.get());
326 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest
, Escape
) {
327 // auth_requestor_1_ should get selected automatically by the
328 // SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have
329 // the same host:port.
330 EXPECT_CALL(*auth_requestor_1_
.get(), CertificateSelected(NULL
));
331 EXPECT_CALL(*auth_requestor_2_
.get(), CertificateSelected(NULL
));
333 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
334 browser(), ui::VKEY_ESCAPE
, false, false, false, false));
336 Mock::VerifyAndClear(auth_requestor_
.get());
337 Mock::VerifyAndClear(auth_requestor_1_
.get());
338 Mock::VerifyAndClear(auth_requestor_2_
.get());
340 // Now let the default selection for auth_requestor_ mock get checked on
342 EXPECT_CALL(*auth_requestor_
.get(), CancelCertificateSelection());
345 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest
, SelectSecond
) {
346 // auth_requestor_1_ should get selected automatically by the
347 // SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have
348 // the same host:port.
349 EXPECT_CALL(*auth_requestor_1_
.get(),
350 CertificateSelected(client_cert_2_
.get()));
351 EXPECT_CALL(*auth_requestor_2_
.get(),
352 CertificateSelected(client_cert_2_
.get()));
354 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
355 browser(), ui::VKEY_DOWN
, false, false, false, false));
357 EXPECT_EQ(client_cert_1_
.get(), selector_
->GetSelectedCert());
358 EXPECT_EQ(client_cert_1_
.get(), selector_1_
->GetSelectedCert());
359 EXPECT_EQ(client_cert_2_
.get(), selector_2_
->GetSelectedCert());
361 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
362 browser(), ui::VKEY_RETURN
, false, false, false, false));
364 Mock::VerifyAndClear(auth_requestor_
.get());
365 Mock::VerifyAndClear(auth_requestor_1_
.get());
366 Mock::VerifyAndClear(auth_requestor_2_
.get());
368 // Now let the default selection for auth_requestor_ mock get checked on
370 EXPECT_CALL(*auth_requestor_
.get(), CancelCertificateSelection());
373 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest
, Escape
) {
374 EXPECT_CALL(*auth_requestor_1_
.get(), CertificateSelected(NULL
));
376 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
377 browser_1_
, ui::VKEY_ESCAPE
, false, false, false, false));
379 Mock::VerifyAndClear(auth_requestor_
.get());
380 Mock::VerifyAndClear(auth_requestor_1_
.get());
382 // Now let the default selection for auth_requestor_ mock get checked on
384 EXPECT_CALL(*auth_requestor_
.get(), CancelCertificateSelection());
387 IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiProfileTest
,
389 EXPECT_CALL(*auth_requestor_1_
.get(),
390 CertificateSelected(client_cert_1_
.get()));
392 EXPECT_TRUE(ui_test_utils::SendKeyPressSync(
393 browser_1_
, ui::VKEY_RETURN
, false, false, false, false));
395 Mock::VerifyAndClear(auth_requestor_
.get());
396 Mock::VerifyAndClear(auth_requestor_1_
.get());
398 // Now let the default selection for auth_requestor_ mock get checked on
400 EXPECT_CALL(*auth_requestor_
.get(), CancelCertificateSelection());