| OLD | NEW |
| (Empty) |
| 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 "components/signin/core/browser/signin_oauth_helper.h" | |
| 16 #include "content/public/browser/navigation_controller.h" | |
| 17 #include "content/public/browser/web_contents_observer.h" | |
| 18 #include "content/public/browser/web_contents_user_data.h" | |
| 19 #include "google_apis/gaia/google_service_auth_error.h" | |
| 20 | |
| 21 class Browser; | |
| 22 class GURL; | |
| 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 namespace password_manager { | |
| 40 class PasswordManager; | |
| 41 } | |
| 42 | |
| 43 // Per-tab one-click signin helper. When a user signs in to a Google service | |
| 44 // and the profile is not yet connected to a Google account, will start the | |
| 45 // process of helping the user connect his profile with one click. The process | |
| 46 // begins with an infobar and is followed with a confirmation dialog explaining | |
| 47 // more about what this means. | |
| 48 class OneClickSigninHelper | |
| 49 : public content::WebContentsObserver, | |
| 50 public content::WebContentsUserData<OneClickSigninHelper> { | |
| 51 public: | |
| 52 // Represents user's decision about sign in process. | |
| 53 enum AutoAccept { | |
| 54 // User decision not yet known. Assume cancel. | |
| 55 AUTO_ACCEPT_NONE, | |
| 56 | |
| 57 // User has explicitly accepted to sign in. A bubble is shown with the | |
| 58 // option to start sync, configure it first, or abort. | |
| 59 AUTO_ACCEPT_ACCEPTED, | |
| 60 | |
| 61 // User has explicitly accepted to sign in, but wants to configure sync | |
| 62 // settings before turning it on. | |
| 63 AUTO_ACCEPT_CONFIGURE, | |
| 64 | |
| 65 // User has explicitly rejected to sign in. Furthermore, the user does | |
| 66 // not want to be prompted to see the interstitial again in this profile. | |
| 67 AUTO_ACCEPT_REJECTED_FOR_PROFILE, | |
| 68 | |
| 69 // This is an explicit sign in from either first run, NTP, wrench menu, | |
| 70 // or settings page. The user will be signed in automatically with sync | |
| 71 // enabled using default settings. | |
| 72 AUTO_ACCEPT_EXPLICIT | |
| 73 }; | |
| 74 | |
| 75 // Return value of CanOfferOnIOThread(). | |
| 76 enum Offer { | |
| 77 CAN_OFFER, | |
| 78 DONT_OFFER, | |
| 79 IGNORE_REQUEST | |
| 80 }; | |
| 81 | |
| 82 // Argument to CanOffer(). | |
| 83 enum CanOfferFor { | |
| 84 CAN_OFFER_FOR_ALL, | |
| 85 CAN_OFFER_FOR_INTERSTITAL_ONLY, | |
| 86 CAN_OFFER_FOR_SECONDARY_ACCOUNT | |
| 87 // TODO(guohui): needs to handle adding secondary account through | |
| 88 // interstitial. | |
| 89 }; | |
| 90 | |
| 91 // Arguments used with StartSync function. base::Bind() cannot support too | |
| 92 // many args for performance reasons, so they are packaged up into a struct. | |
| 93 struct StartSyncArgs { | |
| 94 // Default contructor for testing only. | |
| 95 StartSyncArgs(); | |
| 96 StartSyncArgs(Profile* profile, | |
| 97 Browser* browser, | |
| 98 OneClickSigninHelper::AutoAccept auto_accept, | |
| 99 const std::string& session_index, | |
| 100 const std::string& email, | |
| 101 const std::string& password, | |
| 102 const std::string& refresh_token, | |
| 103 content::WebContents* web_contents, | |
| 104 bool untrusted_confirmation_required, | |
| 105 signin_metrics::Source source, | |
| 106 OneClickSigninSyncStarter::Callback callback); | |
| 107 ~StartSyncArgs(); | |
| 108 | |
| 109 Profile* profile; | |
| 110 Browser* browser; | |
| 111 OneClickSigninHelper::AutoAccept auto_accept; | |
| 112 std::string session_index; | |
| 113 std::string email; | |
| 114 std::string password; | |
| 115 std::string refresh_token; | |
| 116 | |
| 117 // Web contents in which the sync setup page should be displayed, | |
| 118 // if necessary. Can be NULL. | |
| 119 content::WebContents* web_contents; | |
| 120 | |
| 121 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required; | |
| 122 signin_metrics::Source source; | |
| 123 OneClickSigninSyncStarter::Callback callback; | |
| 124 }; | |
| 125 | |
| 126 // Wrapper to call OneClickSigninSyncStarter after fetching the refresh token | |
| 127 // if needed. Also verifies that the cookies are correct if no password is | |
| 128 // specified, and checks that the email from the cookies match the expected | |
| 129 // email address. | |
| 130 class SyncStarterWrapper : public SigninOAuthHelper::Consumer, | |
| 131 public chrome::BrowserListObserver { | |
| 132 public: | |
| 133 SyncStarterWrapper( | |
| 134 const OneClickSigninHelper::StartSyncArgs& args, | |
| 135 OneClickSigninSyncStarter::StartSyncMode start_mode); | |
| 136 ~SyncStarterWrapper() override; | |
| 137 | |
| 138 void Start(); | |
| 139 | |
| 140 private: | |
| 141 void VerifyGaiaCookiesBeforeSignIn(); | |
| 142 void OnGaiaCookiesFetched(const std::string session_index, | |
| 143 const net::CookieList& cookie_list); | |
| 144 | |
| 145 // Virtual to be overridden in tests. | |
| 146 virtual void DisplayErrorBubble(const std::string& error_message); | |
| 147 virtual void StartSigninOAuthHelper(); | |
| 148 virtual void StartOneClickSigninSyncStarter( | |
| 149 const std::string& email, | |
| 150 const std::string& refresh_token); | |
| 151 | |
| 152 // Overriden from SigninOAuthHelper::Consumer. | |
| 153 void OnSigninOAuthInformationAvailable( | |
| 154 const std::string& email, | |
| 155 const std::string& display_email, | |
| 156 const std::string& refresh_token) override; | |
| 157 void OnSigninOAuthInformationFailure( | |
| 158 const GoogleServiceAuthError& error) override; | |
| 159 | |
| 160 // Overriden from chrome::BrowserListObserver. | |
| 161 void OnBrowserRemoved(Browser* browser) override; | |
| 162 | |
| 163 OneClickSigninHelper::StartSyncArgs args_; | |
| 164 chrome::HostDesktopType desktop_type_; | |
| 165 OneClickSigninSyncStarter::StartSyncMode start_mode_; | |
| 166 scoped_ptr<SigninOAuthHelper> signin_oauth_helper_; | |
| 167 base::WeakPtrFactory<SyncStarterWrapper> weak_pointer_factory_; | |
| 168 | |
| 169 DISALLOW_COPY_AND_ASSIGN(SyncStarterWrapper); | |
| 170 }; | |
| 171 | |
| 172 static void LogHistogramValue(int action); | |
| 173 | |
| 174 // Returns true if the one-click signin feature can be offered at this time. | |
| 175 // If |email| is not empty, then the profile is checked to see if it's | |
| 176 // already connected to a google account or if the user has already rejected | |
| 177 // one-click sign-in with this email, in which cases a one click signin | |
| 178 // should not be offered. | |
| 179 // | |
| 180 // If |can_offer_for| is |CAN_OFFER_FOR_INTERSTITAL_ONLY|, then only do the | |
| 181 // checks that would affect the interstitial page. Otherwise, do the checks | |
| 182 // that would affect the interstitial and the explicit sign ins. | |
| 183 // | |
| 184 // Returns in |error_message_id| an explanation as a string resource ID for | |
| 185 // why one-clicked cannot be offered. |error_message_id| is valid only if | |
| 186 // the return value is false. If no explanation is needed, |error_message_id| | |
| 187 // may be null. | |
| 188 static bool CanOffer(content::WebContents* web_contents, | |
| 189 CanOfferFor can_offer_for, | |
| 190 const std::string& email, | |
| 191 std::string* error_message); | |
| 192 | |
| 193 // Returns true if the one-click signin feature can be offered at this time. | |
| 194 // It can be offered if the io_data is not in an incognito window and if the | |
| 195 // origin of |url| is a valid Gaia sign in origin. This function is meant | |
| 196 // to called only from the IO thread. | |
| 197 static Offer CanOfferOnIOThread(net::URLRequest* request, | |
| 198 ProfileIOData* io_data); | |
| 199 | |
| 200 // Looks for the Google-Accounts-SignIn response header, and if found, | |
| 201 // tries to display an infobar in the tab contents identified by the | |
| 202 // child/route id. | |
| 203 static void ShowInfoBarIfPossible(net::URLRequest* request, | |
| 204 ProfileIOData* io_data, | |
| 205 int child_id, | |
| 206 int route_id); | |
| 207 | |
| 208 // Handles cross account sign in error. If the supplied |email| does not match | |
| 209 // the last signed in email of the current profile, then Chrome will show a | |
| 210 // confirmation dialog before starting sync. It returns true if there is a | |
| 211 // cross account error, and false otherwise. | |
| 212 static bool HandleCrossAccountError( | |
| 213 Profile* profile, | |
| 214 const std::string& session_index, | |
| 215 const std::string& email, | |
| 216 const std::string& password, | |
| 217 const std::string& refresh_token, | |
| 218 OneClickSigninHelper::AutoAccept auto_accept, | |
| 219 signin_metrics::Source source, | |
| 220 OneClickSigninSyncStarter::StartSyncMode start_mode, | |
| 221 OneClickSigninSyncStarter::Callback sync_callback); | |
| 222 | |
| 223 static void RedirectToNtpOrAppsPage( | |
| 224 content::WebContents* contents, signin_metrics::Source source); | |
| 225 | |
| 226 // If the |source| is not settings page/webstore, redirects to | |
| 227 // the NTP/Apps page. | |
| 228 static void RedirectToNtpOrAppsPageIfNecessary( | |
| 229 content::WebContents* contents, signin_metrics::Source source); | |
| 230 | |
| 231 // Remove the item currently at the top of the history list if it's | |
| 232 // the Gaia redirect URL. Due to limitations of the NavigationController | |
| 233 // this cannot be done until a new page becomes "current". | |
| 234 static void RemoveSigninRedirectURLHistoryItem( | |
| 235 content::WebContents* web_contents); | |
| 236 | |
| 237 static void LogConfirmHistogramValue(int action); | |
| 238 | |
| 239 private: | |
| 240 friend class content::WebContentsUserData<OneClickSigninHelper>; | |
| 241 friend class OneClickSigninHelperTest; | |
| 242 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIncognitoTest, | |
| 243 ShowInfoBarUIThreadIncognito); | |
| 244 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, | |
| 245 SigninFromWebstoreWithConfigSyncfirst); | |
| 246 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, | |
| 247 ShowSigninBubbleAfterSigninComplete); | |
| 248 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninCancelled); | |
| 249 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, SigninFailed); | |
| 250 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperTest, | |
| 251 CleanTransientStateOnNavigate); | |
| 252 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, CanOfferOnIOThread); | |
| 253 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 254 CanOfferOnIOThreadIncognito); | |
| 255 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 256 CanOfferOnIOThreadNoIOData); | |
| 257 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 258 CanOfferOnIOThreadBadURL); | |
| 259 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 260 CanOfferOnIOThreadDisabled); | |
| 261 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 262 CanOfferOnIOThreadSignedIn); | |
| 263 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 264 CanOfferOnIOThreadEmailNotAllowed); | |
| 265 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 266 CanOfferOnIOThreadEmailAlreadyUsed); | |
| 267 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 268 CreateTestProfileIOData); | |
| 269 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 270 CanOfferOnIOThreadWithRejectedEmail); | |
| 271 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 272 CanOfferOnIOThreadNoSigninCookies); | |
| 273 FRIEND_TEST_ALL_PREFIXES(OneClickSigninHelperIOTest, | |
| 274 CanOfferOnIOThreadDisabledByPolicy); | |
| 275 | |
| 276 // Maximum number of navigations away from the set of valid Gaia URLs before | |
| 277 // clearing the internal state of the helper. This is necessary to support | |
| 278 // SAML-based accounts, but causes bug crbug.com/181163. | |
| 279 static const int kMaxNavigationsSince; | |
| 280 | |
| 281 explicit OneClickSigninHelper(content::WebContents* web_contents); | |
| 282 | |
| 283 ~OneClickSigninHelper() override; | |
| 284 | |
| 285 // Returns true if the one-click signin feature can be offered at this time. | |
| 286 // It can be offered if the io_data is not in an incognito window and if the | |
| 287 // origin of |url| is a valid Gaia sign in origin. This function is meant | |
| 288 // to called only from the IO thread. | |
| 289 static Offer CanOfferOnIOThreadImpl(const GURL& url, | |
| 290 base::SupportsUserData* request, | |
| 291 ProfileIOData* io_data); | |
| 292 | |
| 293 // The portion of ShowInfoBarIfPossible() that needs to run on the UI thread. | |
| 294 // |session_index| and |email| are extracted from the Google-Accounts-SignIn | |
| 295 // header. |auto_accept| is extracted from the Google-Chrome-SignIn header. | |
| 296 // |source| is used to determine which of the explicit sign in mechanism is | |
| 297 // being used. | |
| 298 // | |
| 299 // |continue_url| is where Gaia will continue to when the sign in process is | |
| 300 // done. For explicit sign ins, this is a URL chrome controls. For one-click | |
| 301 // sign in, this could be any google property. This URL is used to know | |
| 302 // when the sign process is over and to collect infomation from the user | |
| 303 // entered on the Gaia sign in page (for explicit sign ins). | |
| 304 static void ShowInfoBarUIThread(const std::string& session_index, | |
| 305 const std::string& email, | |
| 306 AutoAccept auto_accept, | |
| 307 signin_metrics::Source source, | |
| 308 const GURL& continue_url, | |
| 309 int child_id, | |
| 310 int route_id); | |
| 311 | |
| 312 void RedirectToSignin(); | |
| 313 | |
| 314 // Clear all data member of the helper, except for the error. | |
| 315 void CleanTransientState(); | |
| 316 | |
| 317 // Unitests that use a TestingProfile should call this. | |
| 318 // Otherwise, clearing the pending e-mail crashes because the code expects | |
| 319 // a real ResourceContext rather than the MockResourceContext a | |
| 320 // TestingProfile provides. | |
| 321 void SetDoNotClearPendingEmailForTesting(); | |
| 322 | |
| 323 // In unit tests, disable starting the actual sync. | |
| 324 void set_do_not_start_sync_for_testing(); | |
| 325 | |
| 326 // Called when password has been submitted. | |
| 327 void PasswordSubmitted(const autofill::PasswordForm& form); | |
| 328 | |
| 329 // content::WebContentsObserver overrides. | |
| 330 void DidStartNavigationToPendingEntry( | |
| 331 const GURL& url, | |
| 332 content::NavigationController::ReloadType reload_type) override; | |
| 333 void DidNavigateMainFrame( | |
| 334 const content::LoadCommittedDetails& details, | |
| 335 const content::FrameNavigateParams& params) override; | |
| 336 void DidStopLoading(content::RenderViewHost* render_view_host) override; | |
| 337 | |
| 338 // Tracks if we are in the process of showing the signin or one click | |
| 339 // interstitial page. It's set to true the first time we load one of those | |
| 340 // pages and set to false when transient state is cleaned. | |
| 341 // Note: This should only be used for logging purposes. | |
| 342 bool showing_signin_; | |
| 343 | |
| 344 // Information about the account that has just logged in. | |
| 345 std::string session_index_; | |
| 346 std::string email_; | |
| 347 std::string password_; | |
| 348 AutoAccept auto_accept_; | |
| 349 signin_metrics::Source source_; | |
| 350 bool switched_to_advanced_; | |
| 351 GURL continue_url_; | |
| 352 // The orignal continue URL after sync setup is complete. | |
| 353 GURL original_continue_url_; | |
| 354 std::string error_message_; | |
| 355 | |
| 356 // Number of navigations since starting a sign in that is outside the | |
| 357 // the set of trusted Gaia URLs. Sign in attempts that include visits to | |
| 358 // one more untrusted will cause a modal dialog to appear asking the user | |
| 359 // to confirm, similar to the interstitial flow. | |
| 360 int untrusted_navigations_since_signin_visit_; | |
| 361 | |
| 362 // Whether a Gaia URL during the sign in process was not handled by the | |
| 363 // dedicated sign in process (e.g. SAML login, which redirects to a | |
| 364 // non-google-controlled domain). | |
| 365 // This is set to true if at least one such URL is detected. | |
| 366 bool untrusted_confirmation_required_; | |
| 367 | |
| 368 // Allows unittests to avoid accessing the ResourceContext for clearing a | |
| 369 // pending e-mail. | |
| 370 bool do_not_clear_pending_email_; | |
| 371 | |
| 372 // Allows unittest to avoid starting sync for real. | |
| 373 bool do_not_start_sync_for_testing_; | |
| 374 | |
| 375 base::WeakPtrFactory<OneClickSigninHelper> weak_pointer_factory_; | |
| 376 | |
| 377 DISALLOW_COPY_AND_ASSIGN(OneClickSigninHelper); | |
| 378 }; | |
| 379 | |
| 380 #endif // CHROME_BROWSER_UI_SYNC_ONE_CLICK_SIGNIN_HELPER_H_ | |
| OLD | NEW |