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