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/ui/auto_login_infobar_delegate.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop/message_loop.h"
10#include "base/metrics/histogram.h"
11#include "base/prefs/pref_service.h"
12#include "base/strings/utf_string_conversions.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/infobars/infobar_service.h"
15#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
17#include "chrome/browser/signin/signin_manager_factory.h"
18#include "chrome/browser/ui/sync/sync_promo_ui.h"
19#include "chrome/common/chrome_switches.h"
20#include "chrome/common/pref_names.h"
21#include "chrome/common/url_constants.h"
22#include "chrome/grit/generated_resources.h"
23#include "components/google/core/browser/google_util.h"
24#include "components/infobars/core/infobar.h"
25#include "components/signin/core/browser/profile_oauth2_token_service.h"
26#include "content/public/browser/navigation_controller.h"
27#include "content/public/browser/page_navigator.h"
28#include "content/public/browser/web_contents.h"
29#include "content/public/browser/web_contents_observer.h"
30#include "content/public/common/referrer.h"
31#include "google_apis/gaia/gaia_constants.h"
32#include "google_apis/gaia/gaia_urls.h"
33#include "google_apis/gaia/ubertoken_fetcher.h"
34#include "grit/theme_resources.h"
35#include "net/base/escape.h"
36#include "net/url_request/url_request.h"
37#include "ui/base/l10n/l10n_util.h"
38
39#if defined(OS_ANDROID)
40#include "chrome/browser/ui/android/infobars/auto_login_infobar_delegate_android.h"
41#endif
42
43
44// AutoLoginRedirector --------------------------------------------------------
45
46namespace {
47
48// This class is created by the AutoLoginInfoBarDelegate when the user wishes to
49// auto-login.  It holds context information needed while re-issuing service
50// tokens using the OAuth2TokenService, gets the browser cookies with the
51// TokenAuth API, and finally redirects the user to the correct page.
52class AutoLoginRedirector : public UbertokenConsumer,
53                            public content::WebContentsObserver {
54 public:
55  AutoLoginRedirector(content::WebContents* web_contents,
56                      const std::string& args);
57  virtual ~AutoLoginRedirector();
58
59 private:
60  // Overriden from UbertokenConsumer:
61  virtual void OnUbertokenSuccess(const std::string& token) OVERRIDE;
62  virtual void OnUbertokenFailure(const GoogleServiceAuthError& error) OVERRIDE;
63
64  // Implementation of content::WebContentsObserver
65  virtual void WebContentsDestroyed() OVERRIDE;
66
67  // Redirect tab to MergeSession URL, logging the user in and navigating
68  // to the desired page.
69  void RedirectToMergeSession(const std::string& token);
70
71  const std::string args_;
72  scoped_ptr<UbertokenFetcher> ubertoken_fetcher_;
73
74  DISALLOW_COPY_AND_ASSIGN(AutoLoginRedirector);
75};
76
77AutoLoginRedirector::AutoLoginRedirector(
78    content::WebContents* web_contents,
79    const std::string& args)
80    : content::WebContentsObserver(web_contents),
81      args_(args) {
82  Profile* profile =
83      Profile::FromBrowserContext(web_contents->GetBrowserContext());
84  ProfileOAuth2TokenService* token_service =
85      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
86  SigninManagerBase* signin_manager =
87      SigninManagerFactory::GetInstance()->GetForProfile(profile);
88  ubertoken_fetcher_.reset(new UbertokenFetcher(token_service,
89                                                this,
90                                                profile->GetRequestContext()));
91  ubertoken_fetcher_->StartFetchingToken(
92      signin_manager->GetAuthenticatedAccountId());
93}
94
95AutoLoginRedirector::~AutoLoginRedirector() {
96}
97
98void AutoLoginRedirector::WebContentsDestroyed() {
99  // The WebContents that started this has been destroyed. The request must be
100  // cancelled and this object must be deleted.
101  ubertoken_fetcher_.reset();
102  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
103}
104
105void AutoLoginRedirector::OnUbertokenSuccess(const std::string& token) {
106  RedirectToMergeSession(token);
107  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
108}
109
110void AutoLoginRedirector::OnUbertokenFailure(
111    const GoogleServiceAuthError& error) {
112  LOG(WARNING) << "AutoLoginRedirector: token request failed";
113  base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
114}
115
116void AutoLoginRedirector::RedirectToMergeSession(const std::string& token) {
117  // TODO(rogerta): what is the correct page transition?
118  web_contents()->GetController().LoadURL(
119      GaiaUrls::GetInstance()->merge_session_url().Resolve(
120          "?source=chrome&uberauth=" + token + "&" + args_),
121      content::Referrer(), ui::PAGE_TRANSITION_AUTO_BOOKMARK,
122      std::string());
123}
124
125}  // namespace
126
127
128// AutoLoginInfoBarDelegate ---------------------------------------------------
129
130// static
131bool AutoLoginInfoBarDelegate::Create(content::WebContents* web_contents,
132                                      const Params& params) {
133  // If |web_contents| is hosted in a WebDialog, there may be no infobar
134  // service.
135  InfoBarService* infobar_service =
136    InfoBarService::FromWebContents(web_contents);
137  if (!infobar_service)
138    return false;
139
140  Profile* profile =
141      Profile::FromBrowserContext(web_contents->GetBrowserContext());
142#if defined(OS_ANDROID)
143  typedef AutoLoginInfoBarDelegateAndroid Delegate;
144#else
145  typedef AutoLoginInfoBarDelegate Delegate;
146#endif
147  return !!infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
148      scoped_ptr<ConfirmInfoBarDelegate>(new Delegate(params, profile))));
149}
150
151AutoLoginInfoBarDelegate::AutoLoginInfoBarDelegate(const Params& params,
152                                                   Profile* profile)
153    : ConfirmInfoBarDelegate(),
154      params_(params),
155      profile_(profile),
156      button_pressed_(false) {
157  RecordHistogramAction(SHOWN);
158
159  // The AutoLogin infobar is shown in incognito mode on Android, so a
160  // SigninManager isn't guaranteed to exist for |profile_|.
161  SigninManagerBase* signin_manager =
162      SigninManagerFactory::GetInstance()->GetForProfile(profile_);
163  if (signin_manager)
164    signin_manager->AddObserver(this);
165}
166
167AutoLoginInfoBarDelegate::~AutoLoginInfoBarDelegate() {
168  // The AutoLogin infobar is shown in incognito mode on Android, so a
169  // SigninManager isn't guaranteed to exist for |profile_|.
170  SigninManagerBase* signin_manager =
171      SigninManagerFactory::GetInstance()->GetForProfile(profile_);
172  if (signin_manager)
173    signin_manager->RemoveObserver(this);
174
175  if (!button_pressed_)
176    RecordHistogramAction(IGNORED);
177}
178
179void AutoLoginInfoBarDelegate::RecordHistogramAction(Actions action) {
180  UMA_HISTOGRAM_ENUMERATION("AutoLogin.Regular", action,
181                            HISTOGRAM_BOUNDING_VALUE);
182}
183
184void AutoLoginInfoBarDelegate::InfoBarDismissed() {
185  RecordHistogramAction(DISMISSED);
186  button_pressed_ = true;
187}
188
189int AutoLoginInfoBarDelegate::GetIconID() const {
190  return IDR_INFOBAR_AUTOLOGIN;
191}
192
193infobars::InfoBarDelegate::Type AutoLoginInfoBarDelegate::GetInfoBarType()
194    const {
195  return PAGE_ACTION_TYPE;
196}
197
198AutoLoginInfoBarDelegate*
199    AutoLoginInfoBarDelegate::AsAutoLoginInfoBarDelegate() {
200  return this;
201}
202
203base::string16 AutoLoginInfoBarDelegate::GetMessageText() const {
204  return l10n_util::GetStringFUTF16(IDS_AUTOLOGIN_INFOBAR_MESSAGE,
205                                    base::UTF8ToUTF16(params_.username));
206}
207
208base::string16 AutoLoginInfoBarDelegate::GetButtonLabel(
209    InfoBarButton button) const {
210  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
211      IDS_AUTOLOGIN_INFOBAR_OK_BUTTON : IDS_AUTOLOGIN_INFOBAR_CANCEL_BUTTON);
212}
213
214bool AutoLoginInfoBarDelegate::Accept() {
215  // AutoLoginRedirector deletes itself.
216  content::WebContents* web_contents =
217      InfoBarService::WebContentsFromInfoBar(infobar());
218  new AutoLoginRedirector(web_contents, params_.header.args);
219  RecordHistogramAction(ACCEPTED);
220  button_pressed_ = true;
221  return true;
222}
223
224bool AutoLoginInfoBarDelegate::Cancel() {
225  content::WebContents* web_contents =
226      InfoBarService::WebContentsFromInfoBar(infobar());
227  PrefService* pref_service = Profile::FromBrowserContext(
228      web_contents->GetBrowserContext())->GetPrefs();
229  pref_service->SetBoolean(prefs::kAutologinEnabled, false);
230  RecordHistogramAction(REJECTED);
231  button_pressed_ = true;
232  return true;
233}
234
235void AutoLoginInfoBarDelegate::GoogleSignedOut(
236    const std::string& account_id,
237    const std::string& username) {
238  infobar()->RemoveSelf();
239}
240