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 |