1// Copyright 2014 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/password_manager/chrome_password_manager_client.h"
6
7#include "base/bind_helpers.h"
8#include "base/command_line.h"
9#include "base/memory/singleton.h"
10#include "base/metrics/histogram.h"
11#include "base/strings/string16.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/browsing_data/browsing_data_helper.h"
14#include "chrome/browser/password_manager/password_manager_util.h"
15#include "chrome/browser/password_manager/password_store_factory.h"
16#include "chrome/browser/password_manager/save_password_infobar_delegate.h"
17#include "chrome/browser/password_manager/sync_metrics.h"
18#include "chrome/browser/profiles/profile.h"
19#include "chrome/browser/sync/profile_sync_service.h"
20#include "chrome/browser/sync/profile_sync_service_factory.h"
21#include "chrome/browser/ui/autofill/password_generation_popup_controller_impl.h"
22#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h"
23#include "chrome/common/chrome_switches.h"
24#include "chrome/common/chrome_version_info.h"
25#include "chrome/common/url_constants.h"
26#include "components/autofill/content/common/autofill_messages.h"
27#include "components/autofill/core/browser/password_generator.h"
28#include "components/autofill/core/common/password_form.h"
29#include "components/password_manager/content/browser/password_manager_internals_service_factory.h"
30#include "components/password_manager/content/common/credential_manager_messages.h"
31#include "components/password_manager/content/common/credential_manager_types.h"
32#include "components/password_manager/core/browser/log_receiver.h"
33#include "components/password_manager/core/browser/password_form_manager.h"
34#include "components/password_manager/core/browser/password_manager.h"
35#include "components/password_manager/core/browser/password_manager_internals_service.h"
36#include "components/password_manager/core/browser/password_manager_metrics_util.h"
37#include "components/password_manager/core/common/password_manager_switches.h"
38#include "content/public/browser/navigation_entry.h"
39#include "content/public/browser/render_view_host.h"
40#include "content/public/browser/web_contents.h"
41#include "google_apis/gaia/gaia_urls.h"
42#include "net/base/url_util.h"
43#include "third_party/re2/re2/re2.h"
44
45#if defined(OS_ANDROID)
46#include "chrome/browser/android/password_authentication_manager.h"
47#endif  // OS_ANDROID
48
49using password_manager::PasswordManagerInternalsService;
50using password_manager::PasswordManagerInternalsServiceFactory;
51
52DEFINE_WEB_CONTENTS_USER_DATA_KEY(ChromePasswordManagerClient);
53
54// static
55void ChromePasswordManagerClient::CreateForWebContentsWithAutofillClient(
56    content::WebContents* contents,
57    autofill::AutofillClient* autofill_client) {
58  if (FromWebContents(contents))
59    return;
60
61  contents->SetUserData(
62      UserDataKey(),
63      new ChromePasswordManagerClient(contents, autofill_client));
64}
65
66ChromePasswordManagerClient::ChromePasswordManagerClient(
67    content::WebContents* web_contents,
68    autofill::AutofillClient* autofill_client)
69    : content::WebContentsObserver(web_contents),
70      profile_(Profile::FromBrowserContext(web_contents->GetBrowserContext())),
71      driver_(web_contents, this, autofill_client),
72      observer_(NULL),
73      can_use_log_router_(false),
74      autofill_sync_state_(ALLOW_SYNC_CREDENTIALS),
75      sync_credential_was_filtered_(false),
76      weak_factory_(this) {
77  PasswordManagerInternalsService* service =
78      PasswordManagerInternalsServiceFactory::GetForBrowserContext(profile_);
79  if (service)
80    can_use_log_router_ = service->RegisterClient(this);
81  SetUpAutofillSyncState();
82}
83
84ChromePasswordManagerClient::~ChromePasswordManagerClient() {
85  PasswordManagerInternalsService* service =
86      PasswordManagerInternalsServiceFactory::GetForBrowserContext(profile_);
87  if (service)
88    service->UnregisterClient(this);
89}
90
91bool ChromePasswordManagerClient::IsAutomaticPasswordSavingEnabled() const {
92  return CommandLine::ForCurrentProcess()->HasSwitch(
93      password_manager::switches::kEnableAutomaticPasswordSaving) &&
94         chrome::VersionInfo::GetChannel() ==
95             chrome::VersionInfo::CHANNEL_UNKNOWN;
96}
97
98bool ChromePasswordManagerClient::IsPasswordManagerEnabledForCurrentPage()
99    const {
100  DCHECK(web_contents());
101  content::NavigationEntry* entry =
102      web_contents()->GetController().GetLastCommittedEntry();
103  if (!entry) {
104    // TODO(gcasto): Determine if fix for crbug.com/388246 is relevant here.
105    return true;
106  }
107
108  // Disable the password manager for online password management.
109  if (IsURLPasswordWebsiteReauth(entry->GetURL()))
110    return false;
111
112  if (EnabledForSyncSignin())
113    return true;
114
115  // Do not fill nor save password when a user is signing in for sync. This
116  // is because users need to remember their password if they are syncing as
117  // this is effectively their master password.
118  return entry->GetURL().host() != chrome::kChromeUIChromeSigninHost;
119}
120
121bool ChromePasswordManagerClient::ShouldFilterAutofillResult(
122    const autofill::PasswordForm& form) {
123  if (!IsSyncAccountCredential(base::UTF16ToUTF8(form.username_value),
124                               form.signon_realm))
125    return false;
126
127  if (autofill_sync_state_ == DISALLOW_SYNC_CREDENTIALS) {
128    sync_credential_was_filtered_ = true;
129    return true;
130  }
131
132  if (autofill_sync_state_ == DISALLOW_SYNC_CREDENTIALS_FOR_REAUTH &&
133      LastLoadWasTransactionalReauthPage()) {
134    sync_credential_was_filtered_ = true;
135    return true;
136  }
137
138  return false;
139}
140
141bool ChromePasswordManagerClient::IsSyncAccountCredential(
142    const std::string& username, const std::string& origin) const {
143  return password_manager_sync_metrics::IsSyncAccountCredential(
144      profile_, username, origin);
145}
146
147void ChromePasswordManagerClient::AutofillResultsComputed() {
148  UMA_HISTOGRAM_BOOLEAN("PasswordManager.SyncCredentialFiltered",
149                        sync_credential_was_filtered_);
150  sync_credential_was_filtered_ = false;
151}
152
153bool ChromePasswordManagerClient::PromptUserToSavePassword(
154    scoped_ptr<password_manager::PasswordFormManager> form_to_save) {
155  // Save password infobar and the password bubble prompts in case of
156  // "webby" URLs and do not prompt in case of "non-webby" URLS (e.g. file://).
157  if (!BrowsingDataHelper::IsWebScheme(
158      web_contents()->GetLastCommittedURL().scheme())) {
159    return false;
160  }
161
162  if (IsTheHotNewBubbleUIEnabled()) {
163    ManagePasswordsUIController* manage_passwords_ui_controller =
164        ManagePasswordsUIController::FromWebContents(web_contents());
165    manage_passwords_ui_controller->OnPasswordSubmitted(form_to_save.Pass());
166  } else {
167    std::string uma_histogram_suffix(
168        password_manager::metrics_util::GroupIdToString(
169            password_manager::metrics_util::MonitoredDomainGroupId(
170                form_to_save->realm(), GetPrefs())));
171    SavePasswordInfoBarDelegate::Create(
172        web_contents(), form_to_save.Pass(), uma_histogram_suffix);
173  }
174  return true;
175}
176
177void ChromePasswordManagerClient::AutomaticPasswordSave(
178    scoped_ptr<password_manager::PasswordFormManager> saved_form) {
179  if (IsTheHotNewBubbleUIEnabled()) {
180    ManagePasswordsUIController* manage_passwords_ui_controller =
181        ManagePasswordsUIController::FromWebContents(web_contents());
182    manage_passwords_ui_controller->OnAutomaticPasswordSave(
183        saved_form.Pass());
184  }
185}
186
187void ChromePasswordManagerClient::PasswordWasAutofilled(
188    const autofill::PasswordFormMap& best_matches) const {
189  ManagePasswordsUIController* manage_passwords_ui_controller =
190      ManagePasswordsUIController::FromWebContents(web_contents());
191  if (manage_passwords_ui_controller && IsTheHotNewBubbleUIEnabled())
192    manage_passwords_ui_controller->OnPasswordAutofilled(best_matches);
193}
194
195void ChromePasswordManagerClient::PasswordAutofillWasBlocked(
196    const autofill::PasswordFormMap& best_matches) const {
197  ManagePasswordsUIController* controller =
198      ManagePasswordsUIController::FromWebContents(web_contents());
199  if (controller && IsTheHotNewBubbleUIEnabled())
200    controller->OnBlacklistBlockedAutofill(best_matches);
201}
202
203void ChromePasswordManagerClient::AuthenticateAutofillAndFillForm(
204      scoped_ptr<autofill::PasswordFormFillData> fill_data) {
205#if defined(OS_ANDROID)
206  PasswordAuthenticationManager::AuthenticatePasswordAutofill(
207      web_contents(),
208      base::Bind(&ChromePasswordManagerClient::CommitFillPasswordForm,
209                 weak_factory_.GetWeakPtr(),
210                 base::Owned(fill_data.release())));
211#else
212  // Additional authentication is currently only available for Android, so all
213  // other plaftorms should just fill the password form directly.
214  CommitFillPasswordForm(fill_data.get());
215#endif  // OS_ANDROID
216}
217
218void ChromePasswordManagerClient::HidePasswordGenerationPopup() {
219  if (popup_controller_)
220    popup_controller_->HideAndDestroy();
221}
222
223PrefService* ChromePasswordManagerClient::GetPrefs() {
224  return profile_->GetPrefs();
225}
226
227password_manager::PasswordStore*
228ChromePasswordManagerClient::GetPasswordStore() {
229  // Always use EXPLICIT_ACCESS as the password manager checks IsOffTheRecord
230  // itself when it shouldn't access the PasswordStore.
231  // TODO(gcasto): Is is safe to change this to Profile::IMPLICIT_ACCESS?
232  return PasswordStoreFactory::GetForProfile(profile_, Profile::EXPLICIT_ACCESS)
233      .get();
234}
235
236password_manager::PasswordManagerDriver*
237ChromePasswordManagerClient::GetDriver() {
238  return &driver_;
239}
240
241base::FieldTrial::Probability
242ChromePasswordManagerClient::GetProbabilityForExperiment(
243    const std::string& experiment_name) {
244  base::FieldTrial::Probability enabled_probability = 0;
245  if (experiment_name ==
246      password_manager::PasswordManager::kOtherPossibleUsernamesExperiment) {
247    switch (chrome::VersionInfo::GetChannel()) {
248      case chrome::VersionInfo::CHANNEL_DEV:
249      case chrome::VersionInfo::CHANNEL_BETA:
250        enabled_probability = 50;
251        break;
252      default:
253        break;
254    }
255  }
256  return enabled_probability;
257}
258
259bool ChromePasswordManagerClient::IsPasswordSyncEnabled() {
260  ProfileSyncService* sync_service =
261      ProfileSyncServiceFactory::GetForProfile(profile_);
262  // Don't consider sync enabled if the user has a custom passphrase. See
263  // crbug.com/358998 for more details.
264  if (sync_service &&
265      sync_service->HasSyncSetupCompleted() &&
266      sync_service->sync_initialized() &&
267      !sync_service->IsUsingSecondaryPassphrase()) {
268    return sync_service->GetActiveDataTypes().Has(syncer::PASSWORDS);
269  }
270  return false;
271}
272
273void ChromePasswordManagerClient::OnLogRouterAvailabilityChanged(
274    bool router_can_be_used) {
275  if (can_use_log_router_ == router_can_be_used)
276    return;
277  can_use_log_router_ = router_can_be_used;
278
279  NotifyRendererOfLoggingAvailability();
280}
281
282void ChromePasswordManagerClient::LogSavePasswordProgress(
283    const std::string& text) {
284  if (!IsLoggingActive())
285    return;
286  PasswordManagerInternalsService* service =
287      PasswordManagerInternalsServiceFactory::GetForBrowserContext(profile_);
288  if (service)
289    service->ProcessLog(text);
290}
291
292bool ChromePasswordManagerClient::IsLoggingActive() const {
293  // WebUI tabs do not need to log password saving progress. In particular, the
294  // internals page itself should not send any logs.
295  return can_use_log_router_ && !web_contents()->GetWebUI();
296}
297
298void ChromePasswordManagerClient::OnNotifyFailedSignIn(
299    int request_id,
300    const password_manager::CredentialInfo&) {
301  // TODO(mkwst): This is a stub.
302  web_contents()->GetRenderViewHost()->Send(
303      new CredentialManagerMsg_AcknowledgeFailedSignIn(
304          web_contents()->GetRenderViewHost()->GetRoutingID(), request_id));
305}
306
307void ChromePasswordManagerClient::OnNotifySignedIn(
308    int request_id,
309    const password_manager::CredentialInfo&) {
310  // TODO(mkwst): This is a stub.
311  web_contents()->GetRenderViewHost()->Send(
312      new CredentialManagerMsg_AcknowledgeSignedIn(
313          web_contents()->GetRenderViewHost()->GetRoutingID(), request_id));
314}
315
316void ChromePasswordManagerClient::OnNotifySignedOut(int request_id) {
317  // TODO(mkwst): This is a stub.
318  web_contents()->GetRenderViewHost()->Send(
319      new CredentialManagerMsg_AcknowledgeSignedOut(
320          web_contents()->GetRenderViewHost()->GetRoutingID(), request_id));
321}
322
323void ChromePasswordManagerClient::OnRequestCredential(
324    int request_id,
325    bool zero_click_only,
326    const std::vector<GURL>& federations) {
327  // TODO(mkwst): This is a stub.
328  password_manager::CredentialInfo info(base::ASCIIToUTF16("id"),
329                                        base::ASCIIToUTF16("name"),
330                                        GURL("https://example.com/image.png"));
331  web_contents()->GetRenderViewHost()->Send(
332      new CredentialManagerMsg_SendCredential(
333          web_contents()->GetRenderViewHost()->GetRoutingID(),
334          request_id,
335          info));
336}
337
338// static
339password_manager::PasswordGenerationManager*
340ChromePasswordManagerClient::GetGenerationManagerFromWebContents(
341    content::WebContents* contents) {
342  ChromePasswordManagerClient* client =
343      ChromePasswordManagerClient::FromWebContents(contents);
344  if (!client)
345    return NULL;
346  return client->GetDriver()->GetPasswordGenerationManager();
347}
348
349// static
350password_manager::PasswordManager*
351ChromePasswordManagerClient::GetManagerFromWebContents(
352    content::WebContents* contents) {
353  ChromePasswordManagerClient* client =
354      ChromePasswordManagerClient::FromWebContents(contents);
355  if (!client)
356    return NULL;
357  return client->GetDriver()->GetPasswordManager();
358}
359
360void ChromePasswordManagerClient::SetTestObserver(
361    autofill::PasswordGenerationPopupObserver* observer) {
362  observer_ = observer;
363}
364
365bool ChromePasswordManagerClient::OnMessageReceived(
366    const IPC::Message& message) {
367  bool handled = true;
368  IPC_BEGIN_MESSAGE_MAP(ChromePasswordManagerClient, message)
369    // Autofill messages:
370    IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordGenerationPopup,
371                        ShowPasswordGenerationPopup)
372    IPC_MESSAGE_HANDLER(AutofillHostMsg_ShowPasswordEditingPopup,
373                        ShowPasswordEditingPopup)
374    IPC_MESSAGE_HANDLER(AutofillHostMsg_HidePasswordGenerationPopup,
375                        HidePasswordGenerationPopup)
376    IPC_MESSAGE_HANDLER(AutofillHostMsg_PasswordAutofillAgentConstructed,
377                        NotifyRendererOfLoggingAvailability)
378
379    // Credential Manager messages:
380    IPC_MESSAGE_HANDLER(CredentialManagerHostMsg_NotifyFailedSignIn,
381                        OnNotifyFailedSignIn);
382    IPC_MESSAGE_HANDLER(CredentialManagerHostMsg_NotifySignedIn,
383                        OnNotifySignedIn);
384    IPC_MESSAGE_HANDLER(CredentialManagerHostMsg_NotifySignedOut,
385                        OnNotifySignedOut);
386    IPC_MESSAGE_HANDLER(CredentialManagerHostMsg_RequestCredential,
387                        OnRequestCredential);
388
389    // Default:
390    IPC_MESSAGE_UNHANDLED(handled = false)
391  IPC_END_MESSAGE_MAP()
392  return handled;
393}
394
395gfx::RectF ChromePasswordManagerClient::GetBoundsInScreenSpace(
396    const gfx::RectF& bounds) {
397  gfx::Rect client_area = web_contents()->GetContainerBounds();
398  return bounds + client_area.OffsetFromOrigin();
399}
400
401void ChromePasswordManagerClient::ShowPasswordGenerationPopup(
402    const gfx::RectF& bounds,
403    int max_length,
404    const autofill::PasswordForm& form) {
405  // TODO(gcasto): Validate data in PasswordForm.
406
407  // Not yet implemented on other platforms.
408#if defined(USE_AURA) || defined(OS_MACOSX)
409  gfx::RectF element_bounds_in_screen_space = GetBoundsInScreenSpace(bounds);
410
411  popup_controller_ =
412      autofill::PasswordGenerationPopupControllerImpl::GetOrCreate(
413          popup_controller_,
414          element_bounds_in_screen_space,
415          form,
416          max_length,
417          driver_.GetPasswordManager(),
418          observer_,
419          web_contents(),
420          web_contents()->GetNativeView());
421  popup_controller_->Show(true /* display_password */);
422#endif  // defined(USE_AURA) || defined(OS_MACOSX)
423}
424
425void ChromePasswordManagerClient::ShowPasswordEditingPopup(
426    const gfx::RectF& bounds,
427    const autofill::PasswordForm& form) {
428  gfx::RectF element_bounds_in_screen_space = GetBoundsInScreenSpace(bounds);
429  // Not yet implemented on other platforms.
430#if defined(USE_AURA) || defined(OS_MACOSX)
431  popup_controller_ =
432      autofill::PasswordGenerationPopupControllerImpl::GetOrCreate(
433          popup_controller_,
434          element_bounds_in_screen_space,
435          form,
436          0,  // Unspecified max length.
437          driver_.GetPasswordManager(),
438          observer_,
439          web_contents(),
440          web_contents()->GetNativeView());
441  popup_controller_->Show(false /* display_password */);
442#endif  // defined(USE_AURA) || defined(OS_MACOSX)
443}
444
445void ChromePasswordManagerClient::NotifyRendererOfLoggingAvailability() {
446  if (!web_contents())
447    return;
448
449  web_contents()->GetRenderViewHost()->Send(new AutofillMsg_SetLoggingState(
450      web_contents()->GetRenderViewHost()->GetRoutingID(),
451      can_use_log_router_));
452}
453
454void ChromePasswordManagerClient::CommitFillPasswordForm(
455    autofill::PasswordFormFillData* data) {
456  driver_.FillPasswordForm(*data);
457}
458
459bool ChromePasswordManagerClient::LastLoadWasTransactionalReauthPage() const {
460  DCHECK(web_contents());
461  content::NavigationEntry* entry =
462      web_contents()->GetController().GetLastCommittedEntry();
463  if (!entry)
464    return false;
465
466  if (entry->GetURL().GetOrigin() !=
467      GaiaUrls::GetInstance()->gaia_url().GetOrigin())
468    return false;
469
470  // "rart" is the transactional reauth paramter.
471  std::string ignored_value;
472  return net::GetValueForKeyInQuery(entry->GetURL(),
473                                    "rart",
474                                    &ignored_value);
475}
476
477bool ChromePasswordManagerClient::IsURLPasswordWebsiteReauth(
478    const GURL& url) const {
479  if (url.GetOrigin() != GaiaUrls::GetInstance()->gaia_url().GetOrigin())
480    return false;
481
482  // "rart" param signals this page is for transactional reauth.
483  std::string param_value;
484  if (!net::GetValueForKeyInQuery(url, "rart", &param_value))
485    return false;
486
487  // Check the "continue" param to see if this reauth page is for the passwords
488  // website.
489  param_value.clear();
490  if (!net::GetValueForKeyInQuery(url, "continue", &param_value))
491    return false;
492
493  // All password sites, including test sites, have autofilling disabled.
494  CR_DEFINE_STATIC_LOCAL(RE2, account_dashboard_pattern,
495                         ("passwords(-([a-z-]+\\.corp))?\\.google\\.com"));
496
497  return RE2::FullMatch(GURL(param_value).host(), account_dashboard_pattern);
498}
499
500bool ChromePasswordManagerClient::IsTheHotNewBubbleUIEnabled() {
501#if !defined(USE_AURA) && !defined(OS_MACOSX)
502  return false;
503#endif
504  CommandLine* command_line = CommandLine::ForCurrentProcess();
505  if (command_line->HasSwitch(switches::kDisableSavePasswordBubble))
506    return false;
507
508  if (command_line->HasSwitch(switches::kEnableSavePasswordBubble))
509    return true;
510
511  std::string group_name =
512      base::FieldTrialList::FindFullName("PasswordManagerUI");
513
514  // The bubble should be the default case that runs on the bots.
515  return group_name != "Infobar";
516}
517
518bool ChromePasswordManagerClient::EnabledForSyncSignin() {
519  CommandLine* command_line = CommandLine::ForCurrentProcess();
520  if (command_line->HasSwitch(
521          password_manager::switches::kDisableManagerForSyncSignin))
522    return false;
523
524  if (command_line->HasSwitch(
525          password_manager::switches::kEnableManagerForSyncSignin))
526    return true;
527
528  // Default is enabled.
529  std::string group_name =
530      base::FieldTrialList::FindFullName("PasswordManagerStateForSyncSignin");
531  return group_name != "Disabled";
532}
533
534void ChromePasswordManagerClient::SetUpAutofillSyncState() {
535  std::string group_name =
536      base::FieldTrialList::FindFullName("AutofillSyncCredential");
537
538  CommandLine* command_line = CommandLine::ForCurrentProcess();
539  if (command_line->HasSwitch(
540          password_manager::switches::kAllowAutofillSyncCredential)) {
541    autofill_sync_state_ = ALLOW_SYNC_CREDENTIALS;
542    return;
543  }
544  if (command_line->HasSwitch(
545          password_manager::switches::
546          kDisallowAutofillSyncCredentialForReauth)) {
547    autofill_sync_state_ = DISALLOW_SYNC_CREDENTIALS_FOR_REAUTH;
548    return;
549  }
550  if (command_line->HasSwitch(
551          password_manager::switches::kDisallowAutofillSyncCredential)) {
552    autofill_sync_state_ = DISALLOW_SYNC_CREDENTIALS;
553    return;
554  }
555
556  if (group_name == "DisallowSyncCredentialsForReauth") {
557    autofill_sync_state_ = DISALLOW_SYNC_CREDENTIALS_FOR_REAUTH;
558  } else if (group_name == "DisallowSyncCredentials") {
559      autofill_sync_state_ = DISALLOW_SYNC_CREDENTIALS;
560  } else {
561    // Allow by default.
562    autofill_sync_state_ = ALLOW_SYNC_CREDENTIALS;
563  }
564}
565