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.
4
5#include "chrome/browser/ui/website_settings/website_settings.h"
6
7#include "base/at_exit.h"
8#include "base/message_loop/message_loop.h"
9#include "base/strings/string16.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/content_settings/host_content_settings_map.h"
12#include "chrome/browser/infobars/infobar_service.h"
13#include "chrome/browser/ui/website_settings/website_settings_ui.h"
14#include "chrome/test/base/chrome_render_view_host_test_harness.h"
15#include "chrome/test/base/testing_profile.h"
16#include "components/content_settings/core/common/content_settings.h"
17#include "components/content_settings/core/common/content_settings_types.h"
18#include "components/infobars/core/infobar.h"
19#include "content/public/browser/cert_store.h"
20#include "content/public/common/ssl_status.h"
21#include "net/cert/cert_status_flags.h"
22#include "net/cert/x509_certificate.h"
23#include "net/ssl/ssl_connection_status_flags.h"
24#include "net/test/test_certificate_data.h"
25#include "testing/gmock/include/gmock/gmock.h"
26#include "testing/gtest/include/gtest/gtest.h"
27
28using content::SSLStatus;
29using testing::_;
30using testing::AnyNumber;
31using testing::Return;
32using testing::SetArgPointee;
33
34namespace {
35
36// SSL cipher suite like specified in RFC5246 Appendix A.5. "The Cipher Suite".
37// Without the CR_ prefix, this clashes with the OS X 10.8 headers.
38int CR_TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x3D;
39
40int SetSSLVersion(int connection_status, int version) {
41  // Clear SSL version bits (Bits 20, 21 and 22).
42  connection_status &=
43      ~(net::SSL_CONNECTION_VERSION_MASK << net::SSL_CONNECTION_VERSION_SHIFT);
44  int bitmask = version << net::SSL_CONNECTION_VERSION_SHIFT;
45  return bitmask | connection_status;
46}
47
48int SetSSLCipherSuite(int connection_status, int cipher_suite) {
49  // Clear cipher suite bits (the 16 lowest bits).
50  connection_status &= ~net::SSL_CONNECTION_CIPHERSUITE_MASK;
51  return cipher_suite | connection_status;
52}
53
54class MockCertStore : public content::CertStore {
55 public:
56  virtual ~MockCertStore() {}
57  MOCK_METHOD2(StoreCert, int(net::X509Certificate*, int));
58  MOCK_METHOD2(RetrieveCert, bool(int, scoped_refptr<net::X509Certificate>*));
59};
60
61class MockWebsiteSettingsUI : public WebsiteSettingsUI {
62 public:
63  virtual ~MockWebsiteSettingsUI() {}
64  MOCK_METHOD1(SetCookieInfo, void(const CookieInfoList& cookie_info_list));
65  MOCK_METHOD1(SetPermissionInfo,
66               void(const PermissionInfoList& permission_info_list));
67  MOCK_METHOD1(SetIdentityInfo, void(const IdentityInfo& identity_info));
68  MOCK_METHOD1(SetFirstVisit, void(const base::string16& first_visit));
69  MOCK_METHOD1(SetSelectedTab, void(TabId tab_id));
70};
71
72class WebsiteSettingsTest : public ChromeRenderViewHostTestHarness {
73 public:
74  WebsiteSettingsTest() : cert_id_(0), url_("http://www.example.com") {}
75
76  virtual ~WebsiteSettingsTest() {
77  }
78
79  virtual void SetUp() {
80    ChromeRenderViewHostTestHarness::SetUp();
81    // Setup stub SSLStatus.
82    ssl_.security_style = content::SECURITY_STYLE_UNAUTHENTICATED;
83
84    // Create the certificate.
85    cert_id_ = 1;
86    base::Time start_date = base::Time::Now();
87    base::Time expiration_date = base::Time::FromInternalValue(
88        start_date.ToInternalValue() + base::Time::kMicrosecondsPerWeek);
89    cert_ = new net::X509Certificate("subject",
90                                     "issuer",
91                                     start_date,
92                                     expiration_date);
93
94    TabSpecificContentSettings::CreateForWebContents(web_contents());
95    InfoBarService::CreateForWebContents(web_contents());
96
97    // Setup the mock cert store.
98    EXPECT_CALL(cert_store_, RetrieveCert(cert_id_, _) )
99        .Times(AnyNumber())
100        .WillRepeatedly(DoAll(SetArgPointee<1>(cert_), Return(true)));
101
102    // Setup mock ui.
103    mock_ui_.reset(new MockWebsiteSettingsUI());
104  }
105
106  virtual void TearDown() {
107    ASSERT_TRUE(website_settings_.get())
108        << "No WebsiteSettings instance created.";
109    RenderViewHostTestHarness::TearDown();
110    website_settings_.reset();
111  }
112
113  void SetDefaultUIExpectations(MockWebsiteSettingsUI* mock_ui) {
114    // During creation |WebsiteSettings| makes the following calls to the ui.
115    EXPECT_CALL(*mock_ui, SetPermissionInfo(_));
116    EXPECT_CALL(*mock_ui, SetIdentityInfo(_));
117    EXPECT_CALL(*mock_ui, SetCookieInfo(_));
118    EXPECT_CALL(*mock_ui, SetFirstVisit(base::string16()));
119  }
120
121  const GURL& url() const { return url_; }
122  MockCertStore* cert_store() { return &cert_store_; }
123  int cert_id() { return cert_id_; }
124  MockWebsiteSettingsUI* mock_ui() { return mock_ui_.get(); }
125  const SSLStatus& ssl() { return ssl_; }
126  TabSpecificContentSettings* tab_specific_content_settings() {
127    return TabSpecificContentSettings::FromWebContents(web_contents());
128  }
129  InfoBarService* infobar_service() {
130    return InfoBarService::FromWebContents(web_contents());
131  }
132
133  WebsiteSettings* website_settings() {
134    if (!website_settings_.get()) {
135      website_settings_.reset(new WebsiteSettings(
136          mock_ui(), profile(), tab_specific_content_settings(),
137          infobar_service(), url(), ssl(), cert_store()));
138    }
139    return website_settings_.get();
140  }
141
142  SSLStatus ssl_;
143
144 private:
145  scoped_ptr<WebsiteSettings> website_settings_;
146  scoped_ptr<MockWebsiteSettingsUI> mock_ui_;
147  int cert_id_;
148  scoped_refptr<net::X509Certificate> cert_;
149  MockCertStore cert_store_;
150  GURL url_;
151};
152
153}  // namespace
154
155TEST_F(WebsiteSettingsTest, OnPermissionsChanged) {
156  // Setup site permissions.
157  HostContentSettingsMap* content_settings =
158      profile()->GetHostContentSettingsMap();
159  ContentSetting setting = content_settings->GetContentSetting(
160      url(), url(), CONTENT_SETTINGS_TYPE_POPUPS, std::string());
161  EXPECT_EQ(setting, CONTENT_SETTING_BLOCK);
162  setting = content_settings->GetContentSetting(
163      url(), url(), CONTENT_SETTINGS_TYPE_PLUGINS, std::string());
164  EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
165  setting = content_settings->GetContentSetting(
166      url(), url(), CONTENT_SETTINGS_TYPE_GEOLOCATION, std::string());
167  EXPECT_EQ(setting, CONTENT_SETTING_ASK);
168  setting = content_settings->GetContentSetting(
169      url(), url(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string());
170  EXPECT_EQ(setting, CONTENT_SETTING_ASK);
171  setting = content_settings->GetContentSetting(
172      url(), url(), CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string());
173  EXPECT_EQ(setting, CONTENT_SETTING_ASK);
174  setting = content_settings->GetContentSetting(
175      url(), url(), CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string());
176  EXPECT_EQ(setting, CONTENT_SETTING_ASK);
177
178  EXPECT_CALL(*mock_ui(), SetIdentityInfo(_));
179  EXPECT_CALL(*mock_ui(), SetCookieInfo(_));
180  EXPECT_CALL(*mock_ui(), SetFirstVisit(base::string16()));
181
182  // SetPermissionInfo() is called once initially, and then again every time
183  // OnSitePermissionChanged() is called.
184// TODO(markusheintz): This is a temporary hack to fix issue: http://crbug.com/144203.
185#if defined(OS_MACOSX)
186  EXPECT_CALL(*mock_ui(), SetPermissionInfo(_)).Times(6);
187#else
188  EXPECT_CALL(*mock_ui(), SetPermissionInfo(_)).Times(1);
189#endif
190  EXPECT_CALL(*mock_ui(), SetSelectedTab(
191      WebsiteSettingsUI::TAB_ID_PERMISSIONS));
192
193  // Execute code under tests.
194  website_settings()->OnSitePermissionChanged(CONTENT_SETTINGS_TYPE_POPUPS,
195                                              CONTENT_SETTING_ALLOW);
196  website_settings()->OnSitePermissionChanged(CONTENT_SETTINGS_TYPE_PLUGINS,
197                                              CONTENT_SETTING_BLOCK);
198  website_settings()->OnSitePermissionChanged(CONTENT_SETTINGS_TYPE_GEOLOCATION,
199                                              CONTENT_SETTING_ALLOW);
200  website_settings()->OnSitePermissionChanged(
201      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_ALLOW);
202  website_settings()->OnSitePermissionChanged(
203        CONTENT_SETTINGS_TYPE_MEDIASTREAM, CONTENT_SETTING_ALLOW);
204
205  // Verify that the site permissions were changed correctly.
206  setting = content_settings->GetContentSetting(
207      url(), url(), CONTENT_SETTINGS_TYPE_POPUPS, std::string());
208  EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
209  setting = content_settings->GetContentSetting(
210      url(), url(), CONTENT_SETTINGS_TYPE_PLUGINS, std::string());
211  EXPECT_EQ(setting, CONTENT_SETTING_BLOCK);
212  setting = content_settings->GetContentSetting(
213      url(), url(), CONTENT_SETTINGS_TYPE_GEOLOCATION, std::string());
214  EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
215  setting = content_settings->GetContentSetting(
216      url(), url(), CONTENT_SETTINGS_TYPE_NOTIFICATIONS, std::string());
217  EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
218  setting = content_settings->GetContentSetting(
219      url(), url(), CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC, std::string());
220  EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
221  setting = content_settings->GetContentSetting(
222      url(), url(), CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA, std::string());
223  EXPECT_EQ(setting, CONTENT_SETTING_ALLOW);
224}
225
226TEST_F(WebsiteSettingsTest, OnSiteDataAccessed) {
227  EXPECT_CALL(*mock_ui(), SetPermissionInfo(_));
228  EXPECT_CALL(*mock_ui(), SetIdentityInfo(_));
229  EXPECT_CALL(*mock_ui(), SetFirstVisit(base::string16()));
230  EXPECT_CALL(*mock_ui(), SetCookieInfo(_)).Times(2);
231  EXPECT_CALL(*mock_ui(), SetSelectedTab(
232      WebsiteSettingsUI::TAB_ID_PERMISSIONS));
233
234  website_settings()->OnSiteDataAccessed();
235}
236
237TEST_F(WebsiteSettingsTest, HTTPConnection) {
238  SetDefaultUIExpectations(mock_ui());
239  EXPECT_CALL(*mock_ui(), SetSelectedTab(
240      WebsiteSettingsUI::TAB_ID_PERMISSIONS));
241  EXPECT_EQ(WebsiteSettings::SITE_CONNECTION_STATUS_UNENCRYPTED,
242            website_settings()->site_connection_status());
243  EXPECT_EQ(WebsiteSettings::SITE_IDENTITY_STATUS_NO_CERT,
244            website_settings()->site_identity_status());
245  EXPECT_EQ(base::string16(), website_settings()->organization_name());
246}
247
248TEST_F(WebsiteSettingsTest, HTTPSConnection) {
249  ssl_.security_style = content::SECURITY_STYLE_AUTHENTICATED;
250  ssl_.cert_id = cert_id();
251  ssl_.cert_status = 0;
252  ssl_.security_bits = 81;  // No error if > 80.
253  int status = 0;
254  status = SetSSLVersion(status, net::SSL_CONNECTION_VERSION_TLS1);
255  status = SetSSLCipherSuite(status, CR_TLS_RSA_WITH_AES_256_CBC_SHA256);
256  ssl_.connection_status = status;
257
258  SetDefaultUIExpectations(mock_ui());
259  EXPECT_CALL(*mock_ui(), SetSelectedTab(
260      WebsiteSettingsUI::TAB_ID_PERMISSIONS));
261
262  EXPECT_EQ(WebsiteSettings::SITE_CONNECTION_STATUS_ENCRYPTED,
263            website_settings()->site_connection_status());
264  EXPECT_EQ(WebsiteSettings::SITE_IDENTITY_STATUS_CERT,
265            website_settings()->site_identity_status());
266  EXPECT_EQ(base::string16(), website_settings()->organization_name());
267}
268
269TEST_F(WebsiteSettingsTest, HTTPSMixedContent) {
270  ssl_.security_style = content::SECURITY_STYLE_AUTHENTICATED;
271  ssl_.cert_id = cert_id();
272  ssl_.cert_status = 0;
273  ssl_.security_bits = 81;  // No error if > 80.
274  ssl_.content_status = SSLStatus::DISPLAYED_INSECURE_CONTENT;
275  int status = 0;
276  status = SetSSLVersion(status, net::SSL_CONNECTION_VERSION_TLS1);
277  status = SetSSLCipherSuite(status, CR_TLS_RSA_WITH_AES_256_CBC_SHA256);
278  ssl_.connection_status = status;
279
280  SetDefaultUIExpectations(mock_ui());
281  EXPECT_CALL(*mock_ui(), SetSelectedTab(WebsiteSettingsUI::TAB_ID_CONNECTION));
282
283  EXPECT_EQ(WebsiteSettings::SITE_CONNECTION_STATUS_MIXED_CONTENT,
284            website_settings()->site_connection_status());
285  EXPECT_EQ(WebsiteSettings::SITE_IDENTITY_STATUS_CERT,
286            website_settings()->site_identity_status());
287  EXPECT_EQ(base::string16(), website_settings()->organization_name());
288}
289
290TEST_F(WebsiteSettingsTest, HTTPSEVCert) {
291  scoped_refptr<net::X509Certificate> ev_cert =
292      net::X509Certificate::CreateFromBytes(
293          reinterpret_cast<const char*>(google_der),
294          sizeof(google_der));
295  int ev_cert_id = 1;
296  EXPECT_CALL(*cert_store(), RetrieveCert(ev_cert_id, _)).WillRepeatedly(
297      DoAll(SetArgPointee<1>(ev_cert), Return(true)));
298
299  ssl_.security_style = content::SECURITY_STYLE_AUTHENTICATED;
300  ssl_.cert_id = ev_cert_id;
301  ssl_.cert_status = net::CERT_STATUS_IS_EV;
302  ssl_.security_bits = 81;  // No error if > 80.
303  ssl_.content_status = SSLStatus::DISPLAYED_INSECURE_CONTENT;
304  int status = 0;
305  status = SetSSLVersion(status, net::SSL_CONNECTION_VERSION_TLS1);
306  status = SetSSLCipherSuite(status, CR_TLS_RSA_WITH_AES_256_CBC_SHA256);
307  ssl_.connection_status = status;
308
309  SetDefaultUIExpectations(mock_ui());
310  EXPECT_CALL(*mock_ui(), SetSelectedTab(WebsiteSettingsUI::TAB_ID_CONNECTION));
311
312  EXPECT_EQ(WebsiteSettings::SITE_CONNECTION_STATUS_MIXED_CONTENT,
313            website_settings()->site_connection_status());
314  EXPECT_EQ(WebsiteSettings::SITE_IDENTITY_STATUS_EV_CERT,
315            website_settings()->site_identity_status());
316  EXPECT_EQ(base::UTF8ToUTF16("Google Inc"),
317            website_settings()->organization_name());
318}
319
320TEST_F(WebsiteSettingsTest, HTTPSRevocationError) {
321  ssl_.security_style = content::SECURITY_STYLE_AUTHENTICATED;
322  ssl_.cert_id = cert_id();
323  ssl_.cert_status = net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION;
324  ssl_.security_bits = 81;  // No error if > 80.
325  int status = 0;
326  status = SetSSLVersion(status, net::SSL_CONNECTION_VERSION_TLS1);
327  status = SetSSLCipherSuite(status, CR_TLS_RSA_WITH_AES_256_CBC_SHA256);
328  ssl_.connection_status = status;
329
330  SetDefaultUIExpectations(mock_ui());
331  EXPECT_CALL(*mock_ui(), SetSelectedTab(WebsiteSettingsUI::TAB_ID_CONNECTION));
332
333  EXPECT_EQ(WebsiteSettings::SITE_CONNECTION_STATUS_ENCRYPTED,
334            website_settings()->site_connection_status());
335  EXPECT_EQ(WebsiteSettings::SITE_IDENTITY_STATUS_CERT_REVOCATION_UNKNOWN,
336            website_settings()->site_identity_status());
337  EXPECT_EQ(base::string16(), website_settings()->organization_name());
338}
339
340TEST_F(WebsiteSettingsTest, HTTPSConnectionError) {
341  ssl_.security_style = content::SECURITY_STYLE_AUTHENTICATED;
342  ssl_.cert_id = cert_id();
343  ssl_.cert_status = 0;
344  ssl_.security_bits = 1;
345  int status = 0;
346  status = SetSSLVersion(status, net::SSL_CONNECTION_VERSION_TLS1);
347  status = SetSSLCipherSuite(status, CR_TLS_RSA_WITH_AES_256_CBC_SHA256);
348  ssl_.connection_status = status;
349
350  SetDefaultUIExpectations(mock_ui());
351  EXPECT_CALL(*mock_ui(), SetSelectedTab(WebsiteSettingsUI::TAB_ID_CONNECTION));
352
353  EXPECT_EQ(WebsiteSettings::SITE_CONNECTION_STATUS_ENCRYPTED_ERROR,
354            website_settings()->site_connection_status());
355  EXPECT_EQ(WebsiteSettings::SITE_IDENTITY_STATUS_CERT,
356            website_settings()->site_identity_status());
357  EXPECT_EQ(base::string16(), website_settings()->organization_name());
358}
359
360TEST_F(WebsiteSettingsTest, NoInfoBar) {
361  SetDefaultUIExpectations(mock_ui());
362  EXPECT_CALL(*mock_ui(), SetSelectedTab(
363      WebsiteSettingsUI::TAB_ID_PERMISSIONS));
364  EXPECT_EQ(0u, infobar_service()->infobar_count());
365  website_settings()->OnUIClosing();
366  EXPECT_EQ(0u, infobar_service()->infobar_count());
367}
368
369TEST_F(WebsiteSettingsTest, ShowInfoBar) {
370  EXPECT_CALL(*mock_ui(), SetIdentityInfo(_));
371  EXPECT_CALL(*mock_ui(), SetCookieInfo(_));
372  EXPECT_CALL(*mock_ui(), SetFirstVisit(base::string16()));
373
374  // SetPermissionInfo() is called once initially, and then again every time
375  // OnSitePermissionChanged() is called.
376  // TODO(markusheintz): This is a temporary hack to fix issue:
377  // http://crbug.com/144203.
378#if defined(OS_MACOSX)
379  EXPECT_CALL(*mock_ui(), SetPermissionInfo(_)).Times(2);
380#else
381  EXPECT_CALL(*mock_ui(), SetPermissionInfo(_)).Times(1);
382#endif
383
384  EXPECT_CALL(*mock_ui(), SetSelectedTab(
385      WebsiteSettingsUI::TAB_ID_PERMISSIONS));
386  EXPECT_EQ(0u, infobar_service()->infobar_count());
387  website_settings()->OnSitePermissionChanged(
388      CONTENT_SETTINGS_TYPE_GEOLOCATION, CONTENT_SETTING_ALLOW);
389  website_settings()->OnUIClosing();
390  ASSERT_EQ(1u, infobar_service()->infobar_count());
391
392  infobar_service()->RemoveInfoBar(infobar_service()->infobar_at(0));
393}
394