cae1a747e617baaac82ece7b641212de6a475545
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / chromeos / login / gaia_screen_handler.cc
1 // Copyright 2013 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 #include "chrome/browser/ui/webui/chromeos/login/gaia_screen_handler.h"
6
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/browser_shutdown.h"
13 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
14 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
15 #include "chrome/browser/chromeos/login/users/user_manager.h"
16 #include "chrome/browser/chromeos/profiles/profile_helper.h"
17 #include "chrome/browser/chromeos/settings/cros_settings.h"
18 #include "chrome/browser/io_thread.h"
19 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
20 #include "chromeos/chromeos_switches.h"
21 #include "chromeos/settings/cros_settings_names.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_frame_host.h"
24 #include "google_apis/gaia/gaia_auth_util.h"
25 #include "google_apis/gaia/gaia_switches.h"
26 #include "google_apis/gaia/gaia_urls.h"
27 #include "grit/chromium_strings.h"
28 #include "grit/generated_resources.h"
29 #include "ui/base/l10n/l10n_util.h"
30
31 using content::BrowserThread;
32
33 namespace chromeos {
34
35 namespace {
36
37 const char kJsScreenPath[] = "login.GaiaSigninScreen";
38
39 void UpdateAuthParams(base::DictionaryValue* params, bool has_users) {
40   CrosSettings* cros_settings = CrosSettings::Get();
41   bool allow_new_user = true;
42   cros_settings->GetBoolean(kAccountsPrefAllowNewUser, &allow_new_user);
43   bool allow_guest = true;
44   cros_settings->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
45   // Account creation depends on Guest sign-in (http://crosbug.com/24570).
46   params->SetBoolean("createAccount", allow_new_user && allow_guest);
47   params->SetBoolean("guestSignin", allow_guest);
48
49   // Allow locally managed user creation only if:
50   // 1. Enterprise managed device > is allowed by policy.
51   // 2. Consumer device > owner exists.
52   // 3. New users are allowed by owner.
53   // 4. Supervised users are allowed by owner.
54   bool managed_users_allowed =
55       UserManager::Get()->AreLocallyManagedUsersAllowed();
56   bool managed_users_can_create = true;
57   int message_id = -1;
58   if (!has_users) {
59     managed_users_can_create = false;
60     message_id = IDS_CREATE_LOCALLY_MANAGED_USER_NO_MANAGER_TEXT;
61   }
62   if (!allow_new_user || !managed_users_allowed) {
63     managed_users_can_create = false;
64     message_id = IDS_CREATE_LOCALLY_MANAGED_USER_CREATION_RESTRICTED_TEXT;
65   }
66
67   params->SetBoolean("managedUsersEnabled", managed_users_allowed);
68   params->SetBoolean("managedUsersCanCreate", managed_users_can_create);
69   if (!managed_users_can_create) {
70     params->SetString("managedUsersRestrictionReason",
71                       l10n_util::GetStringUTF16(message_id));
72   }
73
74   // Now check whether we're in multi-profiles user adding scenario and
75   // disable GAIA right panel features if that's the case.
76   if (UserAddingScreen::Get()->IsRunning()) {
77     params->SetBoolean("createAccount", false);
78     params->SetBoolean("guestSignin", false);
79     params->SetBoolean("managedUsersEnabled", false);
80   }
81 }
82
83 void RecordSAMLScrapingVerificationResultInHistogram(bool success) {
84   UMA_HISTOGRAM_BOOLEAN("ChromeOS.SAML.Scraping.VerificationResult", success);
85 }
86
87 // The Task posted to PostTaskAndReply in StartClearingDnsCache on the IO
88 // thread.
89 void ClearDnsCache(IOThread* io_thread) {
90   DCHECK_CURRENTLY_ON(BrowserThread::IO);
91   if (browser_shutdown::IsTryingToQuit())
92     return;
93
94   io_thread->ClearHostCache();
95 }
96
97 }  // namespace
98
99 GaiaContext::GaiaContext()
100     : force_reload(false),
101       is_local(false),
102       password_changed(false),
103       show_users(false),
104       use_offline(false),
105       has_users(false) {}
106
107 GaiaScreenHandler::GaiaScreenHandler(
108     const scoped_refptr<NetworkStateInformer>& network_state_informer)
109     : BaseScreenHandler(kJsScreenPath),
110       frame_state_(FRAME_STATE_UNKNOWN),
111       frame_error_(net::OK),
112       network_state_informer_(network_state_informer),
113       dns_cleared_(false),
114       dns_clear_task_running_(false),
115       cookies_cleared_(false),
116       focus_stolen_(false),
117       gaia_silent_load_(false),
118       using_saml_api_(false),
119       test_expects_complete_login_(false),
120       signin_screen_handler_(NULL),
121       weak_factory_(this) {
122   DCHECK(network_state_informer_.get());
123 }
124
125 GaiaScreenHandler::~GaiaScreenHandler() {
126 }
127
128 void GaiaScreenHandler::LoadGaia(const GaiaContext& context) {
129   base::DictionaryValue params;
130
131   params.SetBoolean("forceReload", context.force_reload);
132   params.SetBoolean("isLocal", context.is_local);
133   params.SetBoolean("passwordChanged", context.password_changed);
134   params.SetBoolean("isShowUsers", context.show_users);
135   params.SetBoolean("useOffline", context.use_offline);
136   params.SetString("email", context.email);
137
138   UpdateAuthParams(&params, context.has_users);
139
140   if (!context.use_offline) {
141     const std::string app_locale = g_browser_process->GetApplicationLocale();
142     if (!app_locale.empty())
143       params.SetString("hl", app_locale);
144   } else {
145     base::DictionaryValue* localized_strings = new base::DictionaryValue();
146     localized_strings->SetString(
147         "stringEmail", l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_EMAIL));
148     localized_strings->SetString(
149         "stringPassword",
150         l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_PASSWORD));
151     localized_strings->SetString(
152         "stringSignIn", l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_SIGNIN));
153     localized_strings->SetString(
154         "stringEmptyEmail",
155         l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_EMPTY_EMAIL));
156     localized_strings->SetString(
157         "stringEmptyPassword",
158         l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_EMPTY_PASSWORD));
159     localized_strings->SetString(
160         "stringError", l10n_util::GetStringUTF16(IDS_LOGIN_OFFLINE_ERROR));
161     params.Set("localizedStrings", localized_strings);
162   }
163
164   CommandLine* command_line = CommandLine::ForCurrentProcess();
165
166   const GURL gaia_url =
167       command_line->HasSwitch(::switches::kGaiaUrl)
168           ? GURL(command_line->GetSwitchValueASCII(::switches::kGaiaUrl))
169           : GaiaUrls::GetInstance()->gaia_url();
170   params.SetString("gaiaUrl", gaia_url.spec());
171
172   if (command_line->HasSwitch(chromeos::switches::kEnableEmbeddedSignin))
173     params.SetBoolean("useEmbedded", true);
174
175   frame_state_ = FRAME_STATE_LOADING;
176   CallJS("loadAuthExtension", params);
177 }
178
179 void GaiaScreenHandler::UpdateGaia(const GaiaContext& context) {
180   base::DictionaryValue params;
181   UpdateAuthParams(&params, context.has_users);
182   CallJS("updateAuthExtension", params);
183 }
184
185 void GaiaScreenHandler::ReloadGaia() {
186   if (frame_state_ == FRAME_STATE_LOADING)
187     return;
188   NetworkStateInformer::State state = network_state_informer_->state();
189   if (state != NetworkStateInformer::ONLINE) {
190     LOG(WARNING) << "Skipping reloading of Gaia since "
191                  << "network state="
192                  << NetworkStateInformer::StatusString(state);
193     return;
194   }
195   LOG(WARNING) << "Reloading Gaia.";
196   frame_state_ = FRAME_STATE_LOADING;
197   CallJS("doReload");
198 }
199
200 void GaiaScreenHandler::DeclareLocalizedValues(
201     LocalizedValuesBuilder* builder) {
202   builder->Add("signinScreenTitle", IDS_SIGNIN_SCREEN_TITLE);
203   builder->Add("signinScreenPasswordChanged",
204                IDS_SIGNIN_SCREEN_PASSWORD_CHANGED);
205   builder->Add("createAccount", IDS_CREATE_ACCOUNT_HTML);
206   builder->Add("guestSignin", IDS_BROWSE_WITHOUT_SIGNING_IN_HTML);
207   builder->Add("createLocallyManagedUser",
208                IDS_CREATE_LOCALLY_MANAGED_USER_HTML);
209   builder->Add("createManagedUserFeatureName",
210                IDS_CREATE_LOCALLY_MANAGED_USER_FEATURE_NAME);
211
212   // Strings used by the SAML fatal error dialog.
213   builder->Add("fatalErrorMessageNoEmail", IDS_LOGIN_FATAL_ERROR_NO_EMAIL);
214   builder->Add("fatalErrorMessageNoPassword",
215                IDS_LOGIN_FATAL_ERROR_NO_PASSWORD);
216   builder->Add("fatalErrorMessageVerificationFailed",
217                IDS_LOGIN_FATAL_ERROR_PASSWORD_VERIFICATION);
218   builder->Add("fatalErrorMessageInsecureURL",
219                IDS_LOGIN_FATAL_ERROR_TEXT_INSECURE_URL);
220   builder->Add("fatalErrorInstructions", IDS_LOGIN_FATAL_ERROR_INSTRUCTIONS);
221   builder->Add("fatalErrorDismissButton", IDS_OK);
222 }
223
224 void GaiaScreenHandler::Initialize() {
225 }
226
227 void GaiaScreenHandler::RegisterMessages() {
228   AddCallback("frameLoadingCompleted",
229               &GaiaScreenHandler::HandleFrameLoadingCompleted);
230   AddCallback("completeLogin", &GaiaScreenHandler::HandleCompleteLogin);
231   AddCallback("completeAuthentication",
232               &GaiaScreenHandler::HandleCompleteAuthentication);
233   AddCallback("usingSAMLAPI", &GaiaScreenHandler::HandleUsingSAMLAPI);
234   AddCallback("scrapedPasswordCount",
235               &GaiaScreenHandler::HandleScrapedPasswordCount);
236   AddCallback("scrapedPasswordVerificationFailed",
237               &GaiaScreenHandler::HandleScrapedPasswordVerificationFailed);
238   AddCallback("loginWebuiReady", &GaiaScreenHandler::HandleGaiaUIReady);
239 }
240
241 void GaiaScreenHandler::HandleFrameLoadingCompleted(int status) {
242   const net::Error frame_error = static_cast<net::Error>(-status);
243   if (frame_error == net::ERR_ABORTED) {
244     LOG(WARNING) << "Ignoring Gaia frame error: " << frame_error;
245     return;
246   }
247   frame_error_ = frame_error;
248   if (frame_error == net::OK) {
249     VLOG(1) << "Gaia is loaded";
250     frame_state_ = FRAME_STATE_LOADED;
251   } else {
252     LOG(WARNING) << "Gaia frame error: " << frame_error_;
253     frame_state_ = FRAME_STATE_ERROR;
254   }
255
256   if (network_state_informer_->state() != NetworkStateInformer::ONLINE)
257     return;
258   if (frame_state_ == FRAME_STATE_LOADED)
259     UpdateState(ErrorScreenActor::ERROR_REASON_UPDATE);
260   else if (frame_state_ == FRAME_STATE_ERROR)
261     UpdateState(ErrorScreenActor::ERROR_REASON_FRAME_ERROR);
262 }
263
264 void GaiaScreenHandler::HandleCompleteAuthentication(
265     const std::string& email,
266     const std::string& password,
267     const std::string& auth_code) {
268   if (!Delegate())
269     return;
270   Delegate()->SetDisplayEmail(gaia::SanitizeEmail(email));
271   UserContext user_context(email);
272   user_context.SetKey(Key(password));
273   user_context.SetAuthCode(auth_code);
274   Delegate()->CompleteLogin(user_context);
275 }
276
277 void GaiaScreenHandler::HandleCompleteLogin(const std::string& typed_email,
278                                             const std::string& password,
279                                             bool using_saml) {
280   if (!Delegate())
281     return;
282
283   if (using_saml && !using_saml_api_)
284     RecordSAMLScrapingVerificationResultInHistogram(true);
285
286   const std::string sanitized_email = gaia::SanitizeEmail(typed_email);
287   Delegate()->SetDisplayEmail(sanitized_email);
288   UserContext user_context(sanitized_email);
289   user_context.SetKey(Key(password));
290   user_context.SetAuthFlow(using_saml
291                                ? UserContext::AUTH_FLOW_GAIA_WITH_SAML
292                                : UserContext::AUTH_FLOW_GAIA_WITHOUT_SAML);
293   Delegate()->CompleteLogin(user_context);
294
295   if (test_expects_complete_login_) {
296     VLOG(2) << "Complete test login for " << typed_email
297             << ", requested=" << test_user_;
298
299     test_expects_complete_login_ = false;
300     test_user_.clear();
301     test_pass_.clear();
302   }
303 }
304
305 void GaiaScreenHandler::HandleUsingSAMLAPI() {
306   SetSAMLPrincipalsAPIUsed(true);
307 }
308
309 void GaiaScreenHandler::HandleScrapedPasswordCount(int password_count) {
310   SetSAMLPrincipalsAPIUsed(false);
311   // Use a histogram that has 11 buckets, one for each of the values in [0, 9]
312   // and an overflow bucket at the end.
313   UMA_HISTOGRAM_ENUMERATION(
314       "ChromeOS.SAML.Scraping.PasswordCount", std::min(password_count, 10), 11);
315   if (password_count == 0)
316     HandleScrapedPasswordVerificationFailed();
317 }
318
319 void GaiaScreenHandler::HandleScrapedPasswordVerificationFailed() {
320   RecordSAMLScrapingVerificationResultInHistogram(false);
321 }
322
323 void GaiaScreenHandler::HandleGaiaUIReady() {
324   if (focus_stolen_) {
325     // Set focus to the Gaia page.
326     // TODO(altimofeev): temporary solution, until focus parameters are
327     // implemented on the Gaia side.
328     // Do this only once. Any subsequent call would relod GAIA frame.
329     focus_stolen_ = false;
330     const char code[] =
331         "if (typeof gWindowOnLoad != 'undefined') gWindowOnLoad();";
332     content::RenderFrameHost* frame =
333         LoginDisplayHostImpl::GetGaiaAuthIframe(web_ui()->GetWebContents());
334     frame->ExecuteJavaScript(base::ASCIIToUTF16(code));
335   }
336   if (gaia_silent_load_) {
337     focus_stolen_ = true;
338     // Prevent focus stealing by the Gaia page.
339     // TODO(altimofeev): temporary solution, until focus parameters are
340     // implemented on the Gaia side.
341     const char code[] =
342         "var gWindowOnLoad = window.onload; "
343         "window.onload=function() {};";
344     content::RenderFrameHost* frame =
345         LoginDisplayHostImpl::GetGaiaAuthIframe(web_ui()->GetWebContents());
346     frame->ExecuteJavaScript(base::ASCIIToUTF16(code));
347
348     // As we could miss and window.onload could already be called, restore
349     // focus to current pod (see crbug/175243).
350     DCHECK(signin_screen_handler_);
351     signin_screen_handler_->RefocusCurrentPod();
352   }
353   HandleFrameLoadingCompleted(0);
354
355   if (test_expects_complete_login_)
356     SubmitLoginFormForTest();
357 }
358
359 void GaiaScreenHandler::PopulateEmail(const std::string& user_id) {
360   populated_email_ = user_id;
361 }
362
363 void GaiaScreenHandler::PasswordChangedFor(const std::string& user_id) {
364   password_changed_for_.insert(user_id);
365 }
366
367 void GaiaScreenHandler::StartClearingDnsCache() {
368   if (dns_clear_task_running_ || !g_browser_process->io_thread())
369     return;
370
371   dns_cleared_ = false;
372   BrowserThread::PostTaskAndReply(
373       BrowserThread::IO,
374       FROM_HERE,
375       base::Bind(&ClearDnsCache, g_browser_process->io_thread()),
376       base::Bind(&GaiaScreenHandler::OnDnsCleared, weak_factory_.GetWeakPtr()));
377   dns_clear_task_running_ = true;
378 }
379
380 void GaiaScreenHandler::OnDnsCleared() {
381   DCHECK_CURRENTLY_ON(BrowserThread::UI);
382   dns_clear_task_running_ = false;
383   dns_cleared_ = true;
384   ShowGaiaScreenIfReady();
385 }
386
387 void GaiaScreenHandler::StartClearingCookies(
388     const base::Closure& on_clear_callback) {
389   cookies_cleared_ = false;
390   ProfileHelper* profile_helper =
391       g_browser_process->platform_part()->profile_helper();
392   LOG_ASSERT(Profile::FromWebUI(web_ui()) ==
393              profile_helper->GetSigninProfile());
394   profile_helper->ClearSigninProfile(
395       base::Bind(&GaiaScreenHandler::OnCookiesCleared,
396                  weak_factory_.GetWeakPtr(),
397                  on_clear_callback));
398 }
399
400 void GaiaScreenHandler::OnCookiesCleared(
401     const base::Closure& on_clear_callback) {
402   DCHECK_CURRENTLY_ON(BrowserThread::UI);
403   cookies_cleared_ = true;
404   on_clear_callback.Run();
405 }
406
407 void GaiaScreenHandler::ShowSigninScreenForCreds(const std::string& username,
408                                                  const std::string& password) {
409   VLOG(2) << "ShowSigninScreenForCreds  for user " << username
410           << ", frame_state=" << FrameState();
411
412   test_user_ = username;
413   test_pass_ = password;
414   test_expects_complete_login_ = true;
415
416   // Submit login form for test if gaia is ready. If gaia is loading, login
417   // will be attempted in HandleLoginWebuiReady after gaia is ready. Otherwise,
418   // reload gaia then follow the loading case.
419   if (FrameState() == GaiaScreenHandler::FRAME_STATE_LOADED)
420     SubmitLoginFormForTest();
421   else if (FrameState() != GaiaScreenHandler::FRAME_STATE_LOADING) {
422     DCHECK(signin_screen_handler_);
423     signin_screen_handler_->OnShowAddUser();
424   }
425 }
426
427 void GaiaScreenHandler::SubmitLoginFormForTest() {
428   VLOG(2) << "Submit login form for test, user=" << test_user_;
429
430   std::string code;
431   code += "document.getElementById('Email').value = '" + test_user_ + "';";
432   code += "document.getElementById('Passwd').value = '" + test_pass_ + "';";
433   code += "document.getElementById('signIn').click();";
434
435   content::RenderFrameHost* frame =
436       LoginDisplayHostImpl::GetGaiaAuthIframe(web_ui()->GetWebContents());
437   frame->ExecuteJavaScript(base::ASCIIToUTF16(code));
438
439   // Test properties are cleared in HandleCompleteLogin because the form
440   // submission might fail and login will not be attempted after reloading
441   // if they are cleared here.
442 }
443
444 void GaiaScreenHandler::SetSAMLPrincipalsAPIUsed(bool api_used) {
445   using_saml_api_ = api_used;
446   UMA_HISTOGRAM_BOOLEAN("ChromeOS.SAML.APIUsed", api_used);
447 }
448
449 void GaiaScreenHandler::ShowGaia() {
450   if (gaia_silent_load_ && populated_email_.empty()) {
451     dns_cleared_ = true;
452     cookies_cleared_ = true;
453     ShowGaiaScreenIfReady();
454   } else {
455     StartClearingDnsCache();
456     StartClearingCookies(base::Bind(&GaiaScreenHandler::ShowGaiaScreenIfReady,
457                                     weak_factory_.GetWeakPtr()));
458   }
459 }
460
461 void GaiaScreenHandler::ShowGaiaScreenIfReady() {
462   if (!dns_cleared_ || !cookies_cleared_ || !Delegate())
463     return;
464
465   std::string active_network_path = network_state_informer_->network_path();
466   if (gaia_silent_load_ &&
467       (network_state_informer_->state() != NetworkStateInformer::ONLINE ||
468        gaia_silent_load_network_ != active_network_path)) {
469     // Network has changed. Force Gaia reload.
470     gaia_silent_load_ = false;
471     // Gaia page will be realoded, so focus isn't stolen anymore.
472     focus_stolen_ = false;
473   }
474
475   // Note that LoadAuthExtension clears |populated_email_|.
476   if (populated_email_.empty())
477     Delegate()->LoadSigninWallpaper();
478   else
479     Delegate()->LoadWallpaper(populated_email_);
480
481   // Set Least Recently Used input method for the user.
482   if (!populated_email_.empty())
483     signin_screen_handler_->SetUserInputMethod(populated_email_);
484
485   LoadAuthExtension(!gaia_silent_load_, false, false);
486   signin_screen_handler_->UpdateUIState(
487       SigninScreenHandler::UI_STATE_GAIA_SIGNIN, NULL);
488
489   if (gaia_silent_load_) {
490     // The variable is assigned to false because silently loaded Gaia page was
491     // used.
492     gaia_silent_load_ = false;
493     if (focus_stolen_)
494       HandleGaiaUIReady();
495   }
496
497   signin_screen_handler_->UpdateState(ErrorScreenActor::ERROR_REASON_UPDATE);
498 }
499
500 void GaiaScreenHandler::MaybePreloadAuthExtension() {
501   LOG(WARNING) << "MaybePreloadAuthExtension() call.";
502
503   // If cookies clearing was initiated or |dns_clear_task_running_| then auth
504   // extension showing has already been initiated and preloading is senseless.
505   if (signin_screen_handler_->ShouldLoadGaia() &&
506       !gaia_silent_load_ &&
507       !cookies_cleared_ &&
508       !dns_clear_task_running_ &&
509       network_state_informer_->state() == NetworkStateInformer::ONLINE) {
510     gaia_silent_load_ = true;
511     gaia_silent_load_network_ = network_state_informer_->network_path();
512     LoadAuthExtension(true, true, false);
513   }
514 }
515
516 void GaiaScreenHandler::LoadAuthExtension(bool force,
517                                           bool silent_load,
518                                           bool offline) {
519   GaiaContext context;
520   context.force_reload = force;
521   context.is_local = offline;
522   context.password_changed = !populated_email_.empty() &&
523                              password_changed_for_.count(populated_email_);
524   context.use_offline = offline;
525   context.email = populated_email_;
526   if (Delegate()) {
527     context.show_users = Delegate()->IsShowUsers();
528     context.has_users = !Delegate()->GetUsers().empty();
529   }
530
531   populated_email_.clear();
532
533   LoadGaia(context);
534 }
535
536 void GaiaScreenHandler::UpdateState(ErrorScreenActor::ErrorReason reason) {
537   if (signin_screen_handler_)
538     signin_screen_handler_->UpdateState(reason);
539 }
540
541 SigninScreenHandlerDelegate* GaiaScreenHandler::Delegate() {
542   DCHECK(signin_screen_handler_);
543   return signin_screen_handler_->delegate_;
544 }
545
546 void GaiaScreenHandler::SetSigninScreenHandler(SigninScreenHandler* handler) {
547   signin_screen_handler_ = handler;
548 }
549 }  // namespace chromeos