Home | History | Annotate | Download | only in sync
      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 #ifndef CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_
      6 #define CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_
      7 
      8 #include <string>
      9 
     10 #include "base/gtest_prod_util.h"
     11 #include "base/memory/weak_ptr.h"
     12 #include "chrome/browser/signin/signin_promo.h"
     13 #include "chrome/browser/sync/profile_sync_service_observer.h"
     14 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
     15 #include "content/public/browser/navigation_controller.h"
     16 #include "content/public/browser/web_contents_observer.h"
     17 #include "content/public/browser/web_contents_user_data.h"
     18 #include "google_apis/gaia/google_service_auth_error.h"
     19 
     20 class Browser;
     21 class GURL;
     22 class ProfileIOData;
     23 
     24 namespace content {
     25 class WebContents;
     26 struct FrameNavigateParams;
     27 struct LoadCommittedDetails;
     28 struct PasswordForm;
     29 }
     30 
     31 namespace net {
     32 class URLRequest;
     33 }
     34 
     35 // Per-tab one-click signin helper.  When a user signs in to a Google service
     36 // and the profile is not yet connected to a Google account, will start the
     37 // process of helping the user connect his profile with one click.  The process
     38 // begins with an infobar and is followed with a confirmation dialog explaining
     39 // more about what this means.
     40 class OneClickSigninHelper
     41     : public content::WebContentsObserver,
     42       public content::WebContentsUserData<OneClickSigninHelper>,
     43       public ProfileSyncServiceObserver {
     44  public:
     45   // Represents user's decision about sign in process.
     46   enum AutoAccept {
     47     // User decision not yet known.  Assume cancel.
     48     AUTO_ACCEPT_NONE,
     49 
     50     // User has explicitly accepted to sign in.  A bubble is shown with the
     51     // option to start sync, configure it first, or abort.
     52     AUTO_ACCEPT_ACCEPTED,
     53 
     54     // User has explicitly accepted to sign in, but wants to configure sync
     55     // settings before turning it on.
     56     AUTO_ACCEPT_CONFIGURE,
     57 
     58     // User has explicitly rejected to sign in.  Furthermore, the user does
     59     // not want to be prompted to see the interstitial again in this profile.
     60     AUTO_ACCEPT_REJECTED_FOR_PROFILE,
     61 
     62     // This is an explicit sign in from either first run, NTP, wrench menu,
     63     // or settings page.  The user will be signed in automatically with sync
     64     // enabled using default settings.
     65     AUTO_ACCEPT_EXPLICIT
     66   };
     67 
     68   // Return value of CanOfferOnIOThread().
     69   enum Offer {
     70     CAN_OFFER,
     71     DONT_OFFER,
     72     IGNORE_REQUEST
     73   };
     74 
     75   // Argument to CanOffer().
     76   enum CanOfferFor {
     77     CAN_OFFER_FOR_ALL,
     78     CAN_OFFER_FOR_INTERSTITAL_ONLY
     79   };
     80 
     81   virtual ~OneClickSigninHelper();
     82 
     83   // Returns true if the one-click signin feature can be offered at this time.
     84   // If |email| is not empty, then the profile is checked to see if it's
     85   // already connected to a google account or if the user has already rejected
     86   // one-click sign-in with this email, in which cases a one click signin
     87   // should not be offered.
     88   //
     89   // If |can_offer_for| is |CAN_OFFER_FOR_INTERSTITAL_ONLY|, then only do the
     90   // checks that would affect the interstitial page.  Otherwise, do the checks
     91   // that would affect the interstitial and the explicit sign ins.
     92   //
     93   // Returns in |error_message_id| an explanation as a string resource ID for
     94   // why one-clicked cannot be offered.  |error_message_id| is valid only if
     95   // the return value is false.  If no explanation is needed, |error_message_id|
     96   // may be null.
     97   static bool CanOffer(content::WebContents* web_contents,
     98                        CanOfferFor can_offer_for,
     99                        const std::string& email,
    100                        std::string* error_message);
    101 
    102   // Returns true if the one-click signin feature can be offered at this time.
    103   // It can be offered if the io_data is not in an incognito window and if the
    104   // origin of |url| is a valid Gaia sign in origin.  This function is meant
    105   // to called only from the IO thread.
    106   static Offer CanOfferOnIOThread(net::URLRequest* request,
    107                                   ProfileIOData* io_data);
    108 
    109   // Looks for the Google-Accounts-SignIn response header, and if found,
    110   // tries to display an infobar in the tab contents identified by the
    111   // child/route id.
    112   static void ShowInfoBarIfPossible(net::URLRequest* request,
    113                                     ProfileIOData* io_data,
    114                                     int child_id,
    115                                     int route_id);
    116 
    117   // Remove the item currently at the top of the history list if it's
    118   // the Gaia redirect URL. Due to limitations of the NavigationController
    119   // this cannot be done until a new page becomes "current".
    120   static void RemoveSigninRedirectURLHistoryItem(
    121       content::WebContents* web_contents);
    122 
    123   static void LogConfirmHistogramValue(int action);
    124 
    125  private:
    126   friend class content::WebContentsUserData<OneClickSigninHelper>;
    127   friend class OneClickSigninHelperTest;
    128   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
    129                            ShowInfoBarUIThreadIncognito);
    130   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
    131                            SigninFromWebstoreWithConfigSyncfirst);
    132   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
    133                            ShowSigninBubbleAfterSigninComplete);
    134   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninCancelled);
    135   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninFailed);
    136   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest,
    137                            CleanTransientStateOnNavigate);
    138   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, CanOfferOnIOThread);
    139   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    140                            CanOfferOnIOThreadIncognito);
    141   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    142                            CanOfferOnIOThreadNoIOData);
    143   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    144                            CanOfferOnIOThreadBadURL);
    145   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    146                            CanOfferOnIOThreadReferrer);
    147   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    148                            CanOfferOnIOThreadDisabled);
    149   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    150                            CanOfferOnIOThreadSignedIn);
    151   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    152                            CanOfferOnIOThreadEmailNotAllowed);
    153   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    154                            CanOfferOnIOThreadEmailAlreadyUsed);
    155   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    156                            CreateTestProfileIOData);
    157   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    158                            CanOfferOnIOThreadWithRejectedEmail);
    159   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    160                            CanOfferOnIOThreadNoSigninCookies);
    161   FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest,
    162                            CanOfferOnIOThreadDisabledByPolicy);
    163 
    164   // Maximum number of navigations away from the set of valid Gaia URLs before
    165   // clearing the internal state of the helper.  This is necessary to support
    166   // SAML-based accounts, but causes bug crbug.com/181163.
    167   static const int kMaxNavigationsSince;
    168 
    169   explicit OneClickSigninHelper(content::WebContents* web_contents);
    170 
    171   // Returns true if the one-click signin feature can be offered at this time.
    172   // It can be offered if the io_data is not in an incognito window and if the
    173   // origin of |url| is a valid Gaia sign in origin.  This function is meant
    174   // to called only from the IO thread.
    175   static Offer CanOfferOnIOThreadImpl(const GURL& url,
    176                                       const std::string& referrer,
    177                                       base::SupportsUserData* request,
    178                                       ProfileIOData* io_data);
    179 
    180   // The portion of ShowInfoBarIfPossible() that needs to run on the UI thread.
    181   // |session_index| and |email| are extracted from the Google-Accounts-SignIn
    182   // header.  |auto_accept| is extracted from the Google-Chrome-SignIn header.
    183   // |source| is used to determine which of the explicit sign in mechanism is
    184   // being used.
    185   //
    186   // |continue_url| is where Gaia will continue to when the sign in process is
    187   // done.  For explicit sign ins, this is a URL chrome controls. For one-click
    188   // sign in, this could be any google property.  This URL is used to know
    189   // when the sign process is over and to collect infomation from the user
    190   // entered on the Gaia sign in page (for explicit sign ins).
    191   static void ShowInfoBarUIThread(const std::string& session_index,
    192                                   const std::string& email,
    193                                   AutoAccept auto_accept,
    194                                   signin::Source source,
    195                                   const GURL& continue_url,
    196                                   int child_id,
    197                                   int route_id);
    198 
    199   void RedirectToSignin();
    200   void ShowSigninErrorBubble(Browser* browser, const std::string& error);
    201 
    202   // Clear all data member of the helper, except for the error.
    203   void CleanTransientState();
    204 
    205   // Unitests that use a TestingProfile should call this.
    206   // Otherwise, clearing the pending e-mail crashes because the code expects
    207   // a real ResourceContext rather than the MockResourceContext a
    208   // TestingProfile provides.
    209   void SetDoNotClearPendingEmailForTesting();
    210 
    211   // Grab Gaia password if available.
    212   bool OnFormSubmitted(const content::PasswordForm& form);
    213 
    214   // content::WebContentsObserver overrides.
    215   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
    216   virtual void NavigateToPendingEntry(
    217       const GURL& url,
    218       content::NavigationController::ReloadType reload_type) OVERRIDE;
    219   virtual void DidNavigateMainFrame(
    220       const content::LoadCommittedDetails& details,
    221       const content::FrameNavigateParams& params) OVERRIDE;
    222   virtual void DidStopLoading(
    223       content::RenderViewHost* render_view_host) OVERRIDE;
    224 
    225   // ProfileSyncServiceObserver.
    226   virtual void OnStateChanged() OVERRIDE;
    227 
    228   OneClickSigninSyncStarter::Callback CreateSyncStarterCallback();
    229 
    230   // Callback invoked when OneClickSigninSyncStarter completes sync setup.
    231   void SyncSetupCompletedCallback(
    232       OneClickSigninSyncStarter::SyncSetupResult result);
    233 
    234   // Tracks if we are in the process of showing the signin or one click
    235   // interstitial page. It's set to true the first time we load one of those
    236   // pages and set to false when transient state is cleaned.
    237   // Note: This should only be used for logging purposes.
    238   bool showing_signin_;
    239 
    240   // Information about the account that has just logged in.
    241   std::string session_index_;
    242   std::string email_;
    243   std::string password_;
    244   AutoAccept auto_accept_;
    245   signin::Source source_;
    246   bool switched_to_advanced_;
    247   GURL continue_url_;
    248   // The orignal continue URL after sync setup is complete.
    249   GURL original_continue_url_;
    250   std::string error_message_;
    251 
    252   // Number of navigations since starting a sign in that is outside the
    253   // the set of trusted Gaia URLs.  Sign in attempts that include visits to
    254   // one more untrusted will cause a modal dialog to appear asking the user
    255   // to confirm, similar to the interstitial flow.
    256   int untrusted_navigations_since_signin_visit_;
    257 
    258   // Whether a Gaia URL during the sign in process was not handled by the
    259   // dedicated sign in process (e.g. SAML login, which redirects to a
    260   // non-google-controlled domain).
    261   // This is set to true if at least one such URL is detected.
    262   bool untrusted_confirmation_required_;
    263 
    264   // Allows unittests to avoid accessing the ResourceContext for clearing a
    265   // pending e-mail.
    266   bool do_not_clear_pending_email_;
    267 
    268   base::WeakPtrFactory<OneClickSigninHelper> weak_pointer_factory_;
    269 
    270   DISALLOW_COPY_AND_ASSIGN(OneClickSigninHelper);
    271 };
    272 
    273 #endif  // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_
    274