blob: 689ab68afcef431fdfd1db2907dc84dc1208563d [file] [log] [blame]
// Copyright 2013 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 "chrome/browser/chromeos/app_mode/kiosk_profile_loader.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/login/auth/chrome_login_performer.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chromeos/cryptohome/async_method_caller.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/login/auth/auth_status_consumer.h"
#include "chromeos/login/auth/user_context.h"
#include "chromeos/login/user_names.h"
#include "content/public/browser/browser_thread.h"
#include "google_apis/gaia/gaia_auth_util.h"
using content::BrowserThread;
namespace chromeos {
namespace {
KioskAppLaunchError::Error LoginFailureToKioskAppLaunchError(
const AuthFailure& error) {
switch (error.reason()) {
case AuthFailure::COULD_NOT_MOUNT_TMPFS:
case AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME:
return KioskAppLaunchError::UNABLE_TO_MOUNT;
case AuthFailure::DATA_REMOVAL_FAILED:
return KioskAppLaunchError::UNABLE_TO_REMOVE;
case AuthFailure::USERNAME_HASH_FAILED:
return KioskAppLaunchError::UNABLE_TO_RETRIEVE_HASH;
default:
NOTREACHED();
return KioskAppLaunchError::UNABLE_TO_MOUNT;
}
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// KioskProfileLoader::CryptohomedChecker ensures cryptohome daemon is up
// and running by issuing an IsMounted call. If the call does not go through
// and chromeos::DBUS_METHOD_CALL_SUCCESS is not returned, it will retry after
// some time out and at the maximum five times before it gives up. Upon
// success, it resumes the launch by logging in as a kiosk mode account.
class KioskProfileLoader::CryptohomedChecker
: public base::SupportsWeakPtr<CryptohomedChecker> {
public:
explicit CryptohomedChecker(KioskProfileLoader* loader)
: loader_(loader),
retry_count_(0) {
}
~CryptohomedChecker() {}
void StartCheck() {
chromeos::DBusThreadManager::Get()->GetCryptohomeClient()->IsMounted(
base::Bind(&CryptohomedChecker::OnCryptohomeIsMounted,
AsWeakPtr()));
}
private:
void OnCryptohomeIsMounted(chromeos::DBusMethodCallStatus call_status,
bool is_mounted) {
if (call_status != chromeos::DBUS_METHOD_CALL_SUCCESS) {
const int kMaxRetryTimes = 5;
++retry_count_;
if (retry_count_ > kMaxRetryTimes) {
LOG(ERROR) << "Could not talk to cryptohomed for launching kiosk app.";
ReportCheckResult(KioskAppLaunchError::CRYPTOHOMED_NOT_RUNNING);
return;
}
const int retry_delay_in_milliseconds = 500 * (1 << retry_count_);
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&CryptohomedChecker::StartCheck, AsWeakPtr()),
base::TimeDelta::FromMilliseconds(retry_delay_in_milliseconds));
return;
}
if (is_mounted)
LOG(ERROR) << "Cryptohome is mounted before launching kiosk app.";
// Proceed only when cryptohome is not mounded or running on dev box.
if (!is_mounted || !base::SysInfo::IsRunningOnChromeOS())
ReportCheckResult(KioskAppLaunchError::NONE);
else
ReportCheckResult(KioskAppLaunchError::ALREADY_MOUNTED);
}
void ReportCheckResult(KioskAppLaunchError::Error error) {
if (error == KioskAppLaunchError::NONE)
loader_->LoginAsKioskAccount();
else
loader_->ReportLaunchResult(error);
}
KioskProfileLoader* loader_;
int retry_count_;
DISALLOW_COPY_AND_ASSIGN(CryptohomedChecker);
};
////////////////////////////////////////////////////////////////////////////////
// KioskProfileLoader
KioskProfileLoader::KioskProfileLoader(const std::string& app_user_id,
bool use_guest_mount,
Delegate* delegate)
: user_id_(app_user_id),
use_guest_mount_(use_guest_mount),
delegate_(delegate) {}
KioskProfileLoader::~KioskProfileLoader() {}
void KioskProfileLoader::Start() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
login_performer_.reset();
cryptohomed_checker_.reset(new CryptohomedChecker(this));
cryptohomed_checker_->StartCheck();
}
void KioskProfileLoader::LoginAsKioskAccount() {
login_performer_.reset(new ChromeLoginPerformer(this));
login_performer_->LoginAsKioskAccount(user_id_, use_guest_mount_);
}
void KioskProfileLoader::ReportLaunchResult(KioskAppLaunchError::Error error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (error != KioskAppLaunchError::NONE) {
delegate_->OnProfileLoadFailed(error);
}
}
void KioskProfileLoader::OnAuthSuccess(const UserContext& user_context) {
// LoginPerformer will delete itself.
login_performer_->set_delegate(NULL);
ignore_result(login_performer_.release());
// If we are launching a demo session, we need to start MountGuest with the
// guest username; this is because there are several places in the cros code
// which rely on the username sent to cryptohome to be $guest. Back in Chrome
// we switch this back to the demo user name to correctly identify this
// user as a demo user.
UserContext context = user_context;
if (context.GetUserID() == chromeos::login::kGuestUserName)
context.SetUserID(DemoAppLauncher::kDemoUserName);
LoginUtils::Get()->PrepareProfile(context,
false, // has_auth_cookies
false, // has_active_session
this);
}
void KioskProfileLoader::OnAuthFailure(const AuthFailure& error) {
ReportLaunchResult(LoginFailureToKioskAppLaunchError(error));
}
void KioskProfileLoader::WhiteListCheckFailed(const std::string& email) {
NOTREACHED();
}
void KioskProfileLoader::PolicyLoadFailed() {
ReportLaunchResult(KioskAppLaunchError::POLICY_LOAD_FAILED);
}
void KioskProfileLoader::OnOnlineChecked(
const std::string& email, bool success) {
NOTREACHED();
}
void KioskProfileLoader::OnProfilePrepared(Profile* profile,
bool browser_launched) {
// This object could be deleted any time after successfully reporting
// a profile load, so invalidate the LoginUtils delegate now.
LoginUtils::Get()->DelegateDeleted(this);
delegate_->OnProfileLoaded(profile);
ReportLaunchResult(KioskAppLaunchError::NONE);
}
} // namespace chromeos