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 |