1// Copyright (c) 2011 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/chromeos/login/login_performer.h"
6
7#include <string>
8
9#include "base/logging.h"
10#include "base/message_loop.h"
11#include "base/metrics/histogram.h"
12#include "base/utf_string_conversions.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chromeos/boot_times_loader.h"
15#include "chrome/browser/chromeos/cros/cros_library.h"
16#include "chrome/browser/chromeos/cros/screen_lock_library.h"
17#include "chrome/browser/chromeos/cros_settings_names.h"
18#include "chrome/browser/chromeos/login/login_utils.h"
19#include "chrome/browser/chromeos/login/screen_locker.h"
20#include "chrome/browser/chromeos/user_cros_settings_provider.h"
21#include "chrome/browser/metrics/user_metrics.h"
22#include "chrome/browser/prefs/pref_service.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/profiles/profile_manager.h"
25#include "chrome/common/pref_names.h"
26#include "content/browser/browser_thread.h"
27#include "content/common/notification_service.h"
28#include "content/common/notification_type.h"
29#include "grit/generated_resources.h"
30#include "ui/base/l10n/l10n_util.h"
31#include "ui/base/resource/resource_bundle.h"
32
33namespace chromeos {
34
35// Initialize default LoginPerformer.
36// static
37LoginPerformer* LoginPerformer::default_performer_ = NULL;
38
39LoginPerformer::LoginPerformer(Delegate* delegate)
40    : last_login_failure_(LoginFailure::None()),
41      delegate_(delegate),
42      password_changed_(false),
43      screen_lock_requested_(false),
44      initial_online_auth_pending_(false),
45      method_factory_(this) {
46  DCHECK(default_performer_ == NULL)
47      << "LoginPerformer should have only one instance.";
48  default_performer_ = this;
49}
50
51LoginPerformer::~LoginPerformer() {
52  DVLOG(1) << "Deleting LoginPerformer";
53  DCHECK(default_performer_ != NULL) << "Default instance should exist.";
54  default_performer_ = NULL;
55}
56
57////////////////////////////////////////////////////////////////////////////////
58// LoginPerformer, LoginStatusConsumer implementation:
59
60void LoginPerformer::OnLoginFailure(const LoginFailure& failure) {
61  UserMetrics::RecordAction(UserMetricsAction("Login_Failure"));
62  UMA_HISTOGRAM_ENUMERATION("Login.FailureReason", failure.reason(),
63                            LoginFailure::NUM_FAILURE_REASONS);
64
65  DVLOG(1) << "failure.reason " << failure.reason();
66  DVLOG(1) << "failure.error.state " << failure.error().state();
67
68  last_login_failure_ = failure;
69  if (delegate_) {
70    captcha_.clear();
71    captcha_token_.clear();
72    if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED &&
73        failure.error().state() == GoogleServiceAuthError::CAPTCHA_REQUIRED) {
74      captcha_token_ = failure.error().captcha().token;
75    }
76    delegate_->OnLoginFailure(failure);
77    return;
78  }
79
80  // Consequent online login failure with blocking UI on.
81  // No difference between cases whether screen was locked by the user or
82  // by LoginPerformer except for the very first screen lock while waiting
83  // for online auth. Otherwise it will be SL active > timeout > screen unlock.
84  // Display recoverable error message using ScreenLocker,
85  // force sign out otherwise.
86  if (ScreenLocker::default_screen_locker() && !initial_online_auth_pending_) {
87    ResolveLockLoginFailure();
88    return;
89  }
90  initial_online_auth_pending_ = false;
91
92  // Offline auth - OK, online auth - failed.
93  if (failure.reason() == LoginFailure::NETWORK_AUTH_FAILED) {
94    ResolveInitialNetworkAuthFailure();
95  } else if (failure.reason() == LoginFailure::LOGIN_TIMED_OUT) {
96    VLOG(1) << "Online login timed out. "
97            << "Granting user access based on offline auth only.";
98    // ScreenLock is not active, it's ok to delete itself.
99    MessageLoop::current()->DeleteSoon(FROM_HERE, this);
100  } else {
101    // COULD_NOT_MOUNT_CRYPTOHOME, COULD_NOT_MOUNT_TMPFS:
102    // happens during offline auth only.
103    // UNLOCK_FAILED is used during normal screen lock case.
104    // TODO(nkostylev) DATA_REMOVAL_FAILED - ?
105    NOTREACHED();
106  }
107}
108
109void LoginPerformer::OnLoginSuccess(
110    const std::string& username,
111    const std::string& password,
112    const GaiaAuthConsumer::ClientLoginResult& credentials,
113    bool pending_requests) {
114  UserMetrics::RecordAction(UserMetricsAction("Login_Success"));
115  // 0 - Login success offline and online. It's a new user. or it's an
116  //     existing user and offline auth took longer than online auth.
117  // 1 - Login success offline only. It's an existing user login.
118  UMA_HISTOGRAM_ENUMERATION("Login.SuccessReason", pending_requests, 2);
119
120  VLOG(1) << "LoginSuccess, pending_requests " << pending_requests;
121  if (delegate_) {
122    // After delegate_->OnLoginSuccess(...) is called, delegate_ releases
123    // LoginPerformer ownership. LP now manages it's lifetime on its own.
124    // 2 things could make it exist longer:
125    // 1. ScreenLock active (pending correct new password input)
126    // 2. Pending online auth request.
127    if (!pending_requests)
128      MessageLoop::current()->DeleteSoon(FROM_HERE, this);
129    else
130      initial_online_auth_pending_ = true;
131
132    delegate_->OnLoginSuccess(username,
133                              password,
134                              credentials,
135                              pending_requests);
136    return;
137  } else {
138    // Online login has succeeded.
139    DCHECK(!pending_requests)
140        << "Pending request w/o delegate_ should not happen!";
141    // It is not guaranted, that profile creation has been finished yet. So use
142    // async version here.
143    credentials_ = credentials;
144    ProfileManager::CreateDefaultProfileAsync(this);
145  }
146}
147
148void LoginPerformer::OnProfileCreated(Profile* profile) {
149  CHECK(profile);
150
151  LoginUtils::Get()->FetchCookies(profile, credentials_);
152  LoginUtils::Get()->FetchTokens(profile, credentials_);
153  credentials_ = GaiaAuthConsumer::ClientLoginResult();
154
155  // Don't unlock screen if it was locked while we're waiting
156  // for initial online auth.
157  if (ScreenLocker::default_screen_locker() &&
158      !initial_online_auth_pending_) {
159    DVLOG(1) << "Online login OK - unlocking screen.";
160    RequestScreenUnlock();
161    // Do not delete itself just yet, wait for unlock.
162    // See ResolveScreenUnlocked().
163    return;
164  }
165  initial_online_auth_pending_ = false;
166  // There's nothing else that's holding LP from deleting itself -
167  // no ScreenLock, no pending requests.
168  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
169}
170
171void LoginPerformer::OnOffTheRecordLoginSuccess() {
172  UserMetrics::RecordAction(
173      UserMetricsAction("Login_GuestLoginSuccess"));
174
175  if (delegate_)
176    delegate_->OnOffTheRecordLoginSuccess();
177  else
178    NOTREACHED();
179}
180
181void LoginPerformer::OnPasswordChangeDetected(
182    const GaiaAuthConsumer::ClientLoginResult& credentials) {
183  cached_credentials_ = credentials;
184  if (delegate_) {
185    delegate_->OnPasswordChangeDetected(credentials);
186  } else {
187    last_login_failure_ =
188        LoginFailure::FromNetworkAuthFailure(GoogleServiceAuthError(
189            GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
190    password_changed_ = true;
191    DVLOG(1) << "Password change detected - locking screen.";
192    RequestScreenLock();
193  }
194}
195
196////////////////////////////////////////////////////////////////////////////////
197// LoginPerformer, SignedSettingsHelper::Callback implementation:
198
199void LoginPerformer::OnCheckWhitelistCompleted(SignedSettings::ReturnCode code,
200                                               const std::string& email) {
201  if (code == SignedSettings::SUCCESS) {
202    // Whitelist check passed, continue with authentication.
203    StartAuthentication();
204  } else {
205    if (delegate_)
206      delegate_->WhiteListCheckFailed(email);
207    else
208      NOTREACHED();
209  }
210}
211
212////////////////////////////////////////////////////////////////////////////////
213// LoginPerformer, NotificationObserver implementation:
214//
215
216void LoginPerformer::Observe(NotificationType type,
217                             const NotificationSource& source,
218                             const NotificationDetails& details) {
219  if (type != NotificationType::SCREEN_LOCK_STATE_CHANGED)
220    return;
221
222  bool is_screen_locked = *Details<bool>(details).ptr();
223  if (is_screen_locked) {
224    if (screen_lock_requested_) {
225      screen_lock_requested_ = false;
226      ResolveScreenLocked();
227    }
228  } else {
229    ResolveScreenUnlocked();
230  }
231}
232
233////////////////////////////////////////////////////////////////////////////////
234// LoginPerformer, public:
235
236void LoginPerformer::Login(const std::string& username,
237                           const std::string& password) {
238  username_ = username;
239  password_ = password;
240
241  // Whitelist check is always performed during initial login and
242  // should not be performed when ScreenLock is active (pending online auth).
243  if (!ScreenLocker::default_screen_locker()) {
244    // Must not proceed without signature verification.
245    UserCrosSettingsProvider user_settings;
246    bool trusted_setting_available = user_settings.RequestTrustedAllowNewUser(
247        method_factory_.NewRunnableMethod(&LoginPerformer::Login,
248                                          username,
249                                          password));
250    if (!trusted_setting_available) {
251      // Value of AllowNewUser setting is still not verified.
252      // Another attempt will be invoked after verification completion.
253      return;
254    }
255  }
256
257  if (ScreenLocker::default_screen_locker() ||
258      UserCrosSettingsProvider::cached_allow_new_user()) {
259    // Starts authentication if guest login is allowed or online auth pending.
260    StartAuthentication();
261  } else {
262    // Otherwise, do whitelist check first.
263    PrefService* local_state = g_browser_process->local_state();
264    CHECK(local_state);
265    if (local_state->IsManagedPreference(kAccountsPrefUsers)) {
266      if (UserCrosSettingsProvider::IsEmailInCachedWhitelist(username)) {
267        StartAuthentication();
268      } else {
269        if (delegate_)
270          delegate_->WhiteListCheckFailed(username);
271        else
272          NOTREACHED();
273      }
274    } else {
275      // In case of signed settings: with current implementation we do not
276      // trust whitelist returned by PrefService.  So make separate check.
277      SignedSettingsHelper::Get()->StartCheckWhitelistOp(
278          username, this);
279    }
280  }
281}
282
283void LoginPerformer::LoginOffTheRecord() {
284  authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
285  BrowserThread::PostTask(
286      BrowserThread::UI, FROM_HERE,
287      NewRunnableMethod(authenticator_.get(),
288                        &Authenticator::LoginOffTheRecord));
289}
290
291void LoginPerformer::RecoverEncryptedData(const std::string& old_password) {
292  BrowserThread::PostTask(
293      BrowserThread::UI, FROM_HERE,
294      NewRunnableMethod(authenticator_.get(),
295                        &Authenticator::RecoverEncryptedData,
296                        old_password,
297                        cached_credentials_));
298  cached_credentials_ = GaiaAuthConsumer::ClientLoginResult();
299}
300
301void LoginPerformer::ResyncEncryptedData() {
302  BrowserThread::PostTask(
303      BrowserThread::UI, FROM_HERE,
304      NewRunnableMethod(authenticator_.get(),
305                        &Authenticator::ResyncEncryptedData,
306                        cached_credentials_));
307  cached_credentials_ = GaiaAuthConsumer::ClientLoginResult();
308}
309
310////////////////////////////////////////////////////////////////////////////////
311// LoginPerformer, private:
312
313void LoginPerformer::RequestScreenLock() {
314  DVLOG(1) << "Screen lock requested";
315  // Will receive notifications on screen unlock and delete itself.
316  registrar_.Add(this,
317                 NotificationType::SCREEN_LOCK_STATE_CHANGED,
318                 NotificationService::AllSources());
319  if (ScreenLocker::default_screen_locker()) {
320    DVLOG(1) << "Screen already locked";
321    ResolveScreenLocked();
322  } else {
323    screen_lock_requested_ = true;
324    chromeos::CrosLibrary::Get()->GetScreenLockLibrary()->
325        NotifyScreenLockRequested();
326  }
327}
328
329void LoginPerformer::RequestScreenUnlock() {
330  DVLOG(1) << "Screen unlock requested";
331  if (ScreenLocker::default_screen_locker()) {
332    chromeos::CrosLibrary::Get()->GetScreenLockLibrary()->
333        NotifyScreenUnlockRequested();
334    // Will unsubscribe from notifications once unlock is successful.
335  } else {
336    LOG(ERROR) << "Screen is not locked";
337    NOTREACHED();
338  }
339}
340
341void LoginPerformer::ResolveInitialNetworkAuthFailure() {
342  DVLOG(1) << "auth_error: " << last_login_failure_.error().state();
343
344  switch (last_login_failure_.error().state()) {
345    case GoogleServiceAuthError::CONNECTION_FAILED:
346    case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
347    case GoogleServiceAuthError::TWO_FACTOR:
348    case GoogleServiceAuthError::REQUEST_CANCELED:
349      // Offline auth already done. Online auth will be done next time
350      // or once user accesses web property.
351      VLOG(1) << "Granting user access based on offline auth only. "
352              << "Online login failed with "
353              << last_login_failure_.error().state();
354      // Resolving initial online auth failure, no ScreenLock is active.
355      MessageLoop::current()->DeleteSoon(FROM_HERE, this);
356      return;
357    case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
358      // Offline auth OK, so it might be the case of changed password.
359      password_changed_ = true;
360    case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
361    case GoogleServiceAuthError::ACCOUNT_DELETED:
362    case GoogleServiceAuthError::ACCOUNT_DISABLED:
363      // Access not granted. User has to sign out.
364      // Request screen lock & show error message there.
365    case GoogleServiceAuthError::CAPTCHA_REQUIRED:
366      // User is requested to enter CAPTCHA challenge.
367      captcha_token_ = last_login_failure_.error().captcha().token;
368      RequestScreenLock();
369      return;
370    default:
371      // Unless there's new GoogleServiceAuthErrors state has been added.
372      NOTREACHED();
373      return;
374  }
375}
376
377void LoginPerformer::ResolveLockLoginFailure() {
378  if (last_login_failure_.reason() == LoginFailure::LOGIN_TIMED_OUT) {
379     LOG(WARNING) << "Online login timed out - unlocking screen. "
380                  << "Granting user access based on offline auth only.";
381     RequestScreenUnlock();
382     return;
383   } else if (last_login_failure_.reason() ==
384                  LoginFailure::NETWORK_AUTH_FAILED) {
385     ResolveLockNetworkAuthFailure();
386     return;
387   } else if (last_login_failure_.reason() ==
388                  LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME ||
389              last_login_failure_.reason() ==
390                  LoginFailure::DATA_REMOVAL_FAILED) {
391     LOG(ERROR) << "Cryptohome error, forcing sign out.";
392   } else {
393     // COULD_NOT_MOUNT_TMPFS, UNLOCK_FAILED should not happen here.
394     NOTREACHED();
395   }
396   ScreenLocker::default_screen_locker()->Signout();
397}
398
399void LoginPerformer::ResolveLockNetworkAuthFailure() {
400  DCHECK(ScreenLocker::default_screen_locker())
401      << "ScreenLocker instance doesn't exist.";
402  DCHECK(last_login_failure_.reason() == LoginFailure::NETWORK_AUTH_FAILED);
403
404  string16 msg;
405  bool sign_out_only = false;
406
407  DVLOG(1) << "auth_error: " << last_login_failure_.error().state();
408
409  switch (last_login_failure_.error().state()) {
410    case GoogleServiceAuthError::CONNECTION_FAILED:
411    case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
412    case GoogleServiceAuthError::TWO_FACTOR:
413    case GoogleServiceAuthError::REQUEST_CANCELED:
414      // Offline auth already done. Online auth will be done next time
415      // or once user accesses web property.
416      LOG(WARNING) << "Granting user access based on offline auth only. "
417                   << "Online login failed with "
418                   << last_login_failure_.error().state();
419      RequestScreenUnlock();
420      return;
421    case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
422      // Password change detected.
423      msg = l10n_util::GetStringUTF16(IDS_LOGIN_ERROR_PASSWORD_CHANGED);
424      break;
425    case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
426    case GoogleServiceAuthError::ACCOUNT_DELETED:
427    case GoogleServiceAuthError::ACCOUNT_DISABLED:
428      // Access not granted. User has to sign out.
429      // Show error message using existing screen lock.
430      msg = l10n_util::GetStringUTF16(IDS_LOGIN_ERROR_RESTRICTED);
431      sign_out_only = true;
432      break;
433    case GoogleServiceAuthError::CAPTCHA_REQUIRED:
434      // User is requested to enter CAPTCHA challenge.
435      captcha_token_ = last_login_failure_.error().captcha().token;
436      msg = l10n_util::GetStringUTF16(IDS_LOGIN_ERROR_PASSWORD_CHANGED);
437      ScreenLocker::default_screen_locker()->ShowCaptchaAndErrorMessage(
438          last_login_failure_.error().captcha().image_url,
439          UTF16ToWide(msg));
440      return;
441    default:
442      // Unless there's new GoogleServiceAuthError state has been added.
443      NOTREACHED();
444      break;
445  }
446
447  ScreenLocker::default_screen_locker()->ShowErrorMessage(UTF16ToWide(msg),
448                                                          sign_out_only);
449}
450
451void LoginPerformer::ResolveScreenLocked() {
452  DVLOG(1) << "Screen locked";
453  ResolveLockNetworkAuthFailure();
454}
455
456void LoginPerformer::ResolveScreenUnlocked() {
457  DVLOG(1) << "Screen unlocked";
458  registrar_.RemoveAll();
459  // If screen was unlocked that was for a reason, should delete itself now.
460  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
461}
462
463void LoginPerformer::StartAuthentication() {
464  DVLOG(1) << "Auth started";
465  BootTimesLoader::Get()->AddLoginTimeMarker("AuthStarted", false);
466  Profile* profile = g_browser_process->profile_manager()->GetDefaultProfile();
467  if (delegate_) {
468    authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
469    BrowserThread::PostTask(
470        BrowserThread::UI, FROM_HERE,
471        NewRunnableMethod(authenticator_.get(),
472                          &Authenticator::AuthenticateToLogin,
473                          profile,
474                          username_,
475                          password_,
476                          captcha_token_,
477                          captcha_));
478  } else {
479    DCHECK(authenticator_.get())
480        << "Authenticator instance doesn't exist for login attempt retry.";
481    // At this point offline auth has been successful,
482    // retry online auth, using existing Authenticator instance.
483    BrowserThread::PostTask(
484        BrowserThread::UI, FROM_HERE,
485        NewRunnableMethod(authenticator_.get(),
486                          &Authenticator::RetryAuth,
487                          profile,
488                          username_,
489                          password_,
490                          captcha_token_,
491                          captcha_));
492  }
493  password_.clear();
494}
495
496}  // namespace chromeos
497