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