blob: b11158168c509bf6e801ecbbe6c543456e63c69e [file] [log] [blame]
// Copyright 2014 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.
#include "chromeos/login/auth/login_performer.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager/session_manager_client.h"
#include "chromeos/login/auth/login_event_recorder.h"
#include "components/account_id/account_id.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_names.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "net/cookies/cookie_monster.h"
#include "net/cookies/cookie_store.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
using base::UserMetricsAction;
namespace chromeos {
LoginPerformer::LoginPerformer(scoped_refptr<base::TaskRunner> task_runner,
Delegate* delegate)
: delegate_(delegate),
task_runner_(task_runner),
last_login_failure_(AuthFailure::AuthFailureNone()),
password_changed_(false),
password_changed_callback_count_(0),
auth_mode_(AUTH_MODE_INTERNAL),
weak_factory_(this) {}
LoginPerformer::~LoginPerformer() {
DVLOG(1) << "Deleting LoginPerformer";
if (authenticator_.get())
authenticator_->SetConsumer(NULL);
if (extended_authenticator_.get())
extended_authenticator_->SetConsumer(NULL);
}
////////////////////////////////////////////////////////////////////////////////
// LoginPerformer, AuthStatusConsumer implementation:
void LoginPerformer::OnAuthFailure(const AuthFailure& failure) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
base::RecordAction(UserMetricsAction("Login_Failure"));
UMA_HISTOGRAM_ENUMERATION("Login.FailureReason",
failure.reason(),
AuthFailure::NUM_FAILURE_REASONS);
LOG(ERROR) << "Login failure, reason=" << failure.reason()
<< ", error.state=" << failure.error().state();
last_login_failure_ = failure;
if (delegate_) {
delegate_->SetAuthFlowOffline(user_context_.GetAuthFlow() ==
UserContext::AUTH_FLOW_OFFLINE);
delegate_->OnAuthFailure(failure);
return;
} else {
// COULD_NOT_MOUNT_CRYPTOHOME, COULD_NOT_MOUNT_TMPFS:
// happens during offline auth only.
NOTREACHED();
}
}
void LoginPerformer::OnAuthSuccess(const UserContext& user_context) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
base::RecordAction(UserMetricsAction("Login_Success"));
// Do not distinguish between offline and online success.
UMA_HISTOGRAM_ENUMERATION("Login.SuccessReason", OFFLINE_AND_ONLINE,
NUM_SUCCESS_REASONS);
VLOG(1) << "LoginSuccess hash: " << user_context.GetUserIDHash();
DCHECK(delegate_);
// After delegate_->OnAuthSuccess(...) is called, delegate_ releases
// LoginPerformer ownership. LP now manages it's lifetime on its own.
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
delegate_->OnAuthSuccess(user_context);
}
void LoginPerformer::OnOffTheRecordAuthSuccess() {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
base::RecordAction(UserMetricsAction("Login_GuestLoginSuccess"));
if (delegate_)
delegate_->OnOffTheRecordAuthSuccess();
else
NOTREACHED();
}
void LoginPerformer::OnPasswordChangeDetected() {
password_changed_ = true;
password_changed_callback_count_++;
if (delegate_) {
delegate_->OnPasswordChangeDetected();
} else {
NOTREACHED();
}
}
void LoginPerformer::OnOldEncryptionDetected(const UserContext& user_context,
bool has_incomplete_migration) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (delegate_)
delegate_->OnOldEncryptionDetected(user_context, has_incomplete_migration);
else
NOTREACHED();
}
////////////////////////////////////////////////////////////////////////////////
// LoginPerformer, public:
void LoginPerformer::NotifyWhitelistCheckFailure() {
if (delegate_)
delegate_->WhiteListCheckFailed(
user_context_.GetAccountId().GetUserEmail());
else
NOTREACHED();
}
void LoginPerformer::PerformLogin(const UserContext& user_context,
AuthorizationMode auth_mode) {
auth_mode_ = auth_mode;
user_context_ = user_context;
if (RunTrustedCheck(base::Bind(&LoginPerformer::DoPerformLogin,
weak_factory_.GetWeakPtr(),
user_context_,
auth_mode))) {
return;
}
DoPerformLogin(user_context_, auth_mode);
}
void LoginPerformer::DoPerformLogin(const UserContext& user_context,
AuthorizationMode auth_mode) {
bool wildcard_match = false;
const AccountId& account_id = user_context.GetAccountId();
if (!IsUserWhitelisted(account_id, &wildcard_match)) {
NotifyWhitelistCheckFailure();
return;
}
if (user_context.GetAuthFlow() == UserContext::AUTH_FLOW_EASY_UNLOCK)
SetupEasyUnlockUserFlow(user_context.GetAccountId());
switch (auth_mode_) {
case AUTH_MODE_EXTENSION: {
RunOnlineWhitelistCheck(
account_id, wildcard_match, user_context.GetRefreshToken(),
base::Bind(&LoginPerformer::StartLoginCompletion,
weak_factory_.GetWeakPtr()),
base::Bind(&LoginPerformer::NotifyWhitelistCheckFailure,
weak_factory_.GetWeakPtr()));
break;
}
case AUTH_MODE_INTERNAL:
StartAuthentication();
break;
}
}
void LoginPerformer::LoginAsSupervisedUser(const UserContext& user_context) {
DCHECK_EQ(
user_manager::kSupervisedUserDomain,
gaia::ExtractDomainName(user_context.GetAccountId().GetUserEmail()));
user_context_ = user_context;
if (user_context_.GetUserType() != user_manager::USER_TYPE_SUPERVISED) {
LOG(FATAL) << "Incorrect supervised user type "
<< user_context_.GetUserType();
}
if (RunTrustedCheck(base::Bind(&LoginPerformer::TrustedLoginAsSupervisedUser,
weak_factory_.GetWeakPtr(),
user_context_))) {
return;
}
TrustedLoginAsSupervisedUser(user_context_);
}
void LoginPerformer::TrustedLoginAsSupervisedUser(
const UserContext& user_context) {
if (!AreSupervisedUsersAllowed()) {
LOG(ERROR) << "Login attempt of supervised user detected.";
delegate_->WhiteListCheckFailed(user_context.GetAccountId().GetUserEmail());
return;
}
SetupSupervisedUserFlow(user_context.GetAccountId());
UserContext user_context_copy = TransformSupervisedKey(user_context);
if (UseExtendedAuthenticatorForSupervisedUser(user_context)) {
EnsureExtendedAuthenticator();
// TODO(antrim) : Replace empty callback with explicit method.
// http://crbug.com/351268
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&ExtendedAuthenticator::AuthenticateToMount,
extended_authenticator_.get(), user_context_copy,
ExtendedAuthenticator::ResultCallback()));
} else {
EnsureAuthenticator();
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Authenticator::LoginAsSupervisedUser,
authenticator_.get(), user_context_copy));
}
}
void LoginPerformer::LoginAsPublicSession(const UserContext& user_context) {
if (!CheckPolicyForUser(user_context.GetAccountId())) {
DCHECK(delegate_);
if (delegate_)
delegate_->PolicyLoadFailed();
return;
}
EnsureAuthenticator();
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Authenticator::LoginAsPublicSession,
authenticator_.get(), user_context));
}
void LoginPerformer::LoginOffTheRecord() {
EnsureAuthenticator();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&Authenticator::LoginOffTheRecord, authenticator_.get()));
}
void LoginPerformer::LoginAsKioskAccount(const AccountId& app_account_id,
bool use_guest_mount) {
EnsureAuthenticator();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&Authenticator::LoginAsKioskAccount, authenticator_.get(),
app_account_id, use_guest_mount));
}
void LoginPerformer::LoginAsArcKioskAccount(
const AccountId& arc_app_account_id) {
EnsureAuthenticator();
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&Authenticator::LoginAsArcKioskAccount,
authenticator_.get(), arc_app_account_id));
}
void LoginPerformer::RecoverEncryptedData(const std::string& old_password) {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Authenticator::RecoverEncryptedData,
authenticator_.get(), old_password));
}
void LoginPerformer::ResyncEncryptedData() {
task_runner_->PostTask(FROM_HERE,
base::BindOnce(&Authenticator::ResyncEncryptedData,
authenticator_.get()));
}
////////////////////////////////////////////////////////////////////////////////
// LoginPerformer, private:
void LoginPerformer::EnsureExtendedAuthenticator() {
if (extended_authenticator_.get())
extended_authenticator_->SetConsumer(NULL);
extended_authenticator_ = ExtendedAuthenticator::Create(this);
}
void LoginPerformer::StartLoginCompletion() {
VLOG(1) << "Online login completion started.";
chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("AuthStarted", false);
content::BrowserContext* browser_context = GetSigninContext();
EnsureAuthenticator();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&chromeos::Authenticator::CompleteLogin,
authenticator_.get(), browser_context, user_context_));
user_context_.ClearSecrets();
}
void LoginPerformer::StartAuthentication() {
VLOG(1) << "Offline auth started.";
chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("AuthStarted", false);
if (delegate_) {
EnsureAuthenticator();
content::BrowserContext* browser_context = GetSigninContext();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&Authenticator::AuthenticateToLogin,
authenticator_.get(), base::Unretained(browser_context),
user_context_));
} else {
NOTREACHED();
}
user_context_.ClearSecrets();
}
void LoginPerformer::EnsureAuthenticator() {
authenticator_ = CreateAuthenticator();
}
} // namespace chromeos