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