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