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