blob: 7a38e33bcb9b94176549522a9be512634cf42013 [file] [log] [blame]
// Copyright 2018 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/login/demo_mode/demo_setup_controller.h"
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_resources.h"
#include "chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/policy/device_local_account_policy_service.h"
#include "chrome/browser/chromeos/policy/enrollment_config.h"
#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/settings/install_attributes.h"
#include "chromeos/system/statistics_provider.h"
#include "components/arc/arc_util.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "ui/base/l10n/l10n_util.h"
namespace chromeos {
namespace {
using ErrorCode = DemoSetupController::DemoSetupError::ErrorCode;
using RecoveryMethod = DemoSetupController::DemoSetupError::RecoveryMethod;
constexpr char kDemoRequisition[] = "cros-demo-mode";
constexpr char kOfflineDevicePolicyFileName[] = "device_policy";
constexpr char kOfflineDeviceLocalAccountPolicyFileName[] =
"local_account_policy";
// The policy blob data for offline demo-mode is embedded into the filesystem.
// TODO(mukai, agawronska): fix this when switching to dm-verity image.
constexpr const base::FilePath::CharType kOfflineDemoModeDir[] =
FILE_PATH_LITERAL("/usr/share/demo_mode_resources/policy");
bool CheckOfflinePolicyFilesExist(const base::FilePath& policy_dir,
std::string* message) {
base::FilePath device_policy_path =
policy_dir.AppendASCII(kOfflineDevicePolicyFileName);
if (!base::PathExists(device_policy_path)) {
*message = base::StringPrintf("Path %s does not exist",
device_policy_path.AsUTF8Unsafe().c_str());
return false;
}
base::FilePath local_account_policy_path =
policy_dir.AppendASCII(kOfflineDeviceLocalAccountPolicyFileName);
if (!base::PathExists(local_account_policy_path)) {
*message =
base::StringPrintf("Path %s does not exist",
local_account_policy_path.AsUTF8Unsafe().c_str());
return false;
}
return true;
}
// Get the DeviceLocalAccountPolicyStore for the account_id.
policy::CloudPolicyStore* GetDeviceLocalAccountPolicyStore(
const std::string& account_id) {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
if (!connector)
return nullptr;
policy::DeviceLocalAccountPolicyService* local_account_service =
connector->GetDeviceLocalAccountPolicyService();
if (!local_account_service)
return nullptr;
const std::string user_id = policy::GenerateDeviceLocalAccountUserId(
account_id, policy::DeviceLocalAccount::TYPE_PUBLIC_SESSION);
policy::DeviceLocalAccountPolicyBroker* broker =
local_account_service->GetBrokerForUser(user_id);
if (!broker)
return nullptr;
return broker->core()->store();
}
// A utility function of base::ReadFileToString which returns an optional
// string.
// TODO(mukai): move this to base/files.
base::Optional<std::string> ReadFileToOptionalString(
const base::FilePath& file_path) {
std::string content;
base::Optional<std::string> result;
if (base::ReadFileToString(file_path, &content))
result = std::move(content);
return result;
}
// Returns whether online FRE check is required.
bool IsOnlineFreCheckRequired() {
AutoEnrollmentController::FRERequirement fre_requirement =
AutoEnrollmentController::GetFRERequirement();
bool enrollment_check_required =
fre_requirement !=
AutoEnrollmentController::FRERequirement::kExplicitlyNotRequired &&
fre_requirement !=
AutoEnrollmentController::FRERequirement::kNotRequired &&
AutoEnrollmentController::IsFREEnabled();
if (!enrollment_check_required)
return false;
std::string block_dev_mode_value;
system::StatisticsProvider* provider =
system::StatisticsProvider::GetInstance();
provider->GetMachineStatistic(system::kBlockDevModeKey,
&block_dev_mode_value);
return block_dev_mode_value == "1";
}
DemoSetupController::DemoSetupError CreateFromClientStatus(
policy::DeviceManagementStatus status,
const std::string& debug_message) {
switch (status) {
case policy::DM_STATUS_SUCCESS:
return DemoSetupController::DemoSetupError(
ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_REQUEST_INVALID:
return DemoSetupController::DemoSetupError(
ErrorCode::kInvalidRequest, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_REQUEST_FAILED:
return DemoSetupController::DemoSetupError(
ErrorCode::kRequestNetworkError, RecoveryMethod::kCheckNetwork,
debug_message);
case policy::DM_STATUS_TEMPORARY_UNAVAILABLE:
return DemoSetupController::DemoSetupError(
ErrorCode::kTemporaryUnavailable, RecoveryMethod::kRetry,
debug_message);
case policy::DM_STATUS_HTTP_STATUS_ERROR:
return DemoSetupController::DemoSetupError(
ErrorCode::kResponseError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_RESPONSE_DECODING_ERROR:
return DemoSetupController::DemoSetupError(
ErrorCode::kResponseDecodingError, RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
case policy::DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE:
case policy::DM_STATUS_SERVICE_ACTIVATION_PENDING:
return DemoSetupController::DemoSetupError(ErrorCode::kDemoAccountError,
RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND:
return DemoSetupController::DemoSetupError(
ErrorCode::kDeviceNotFound, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID:
return DemoSetupController::DemoSetupError(
ErrorCode::kInvalidDMToken, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER:
return DemoSetupController::DemoSetupError(
ErrorCode::kInvalidSerialNumber, RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
return DemoSetupController::DemoSetupError(
ErrorCode::kDeviceIdError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_MISSING_LICENSES:
return DemoSetupController::DemoSetupError(
ErrorCode::kLicenseError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_DEPROVISIONED:
return DemoSetupController::DemoSetupError(
ErrorCode::kDeviceDeprovisioned, RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_DOMAIN_MISMATCH:
return DemoSetupController::DemoSetupError(
ErrorCode::kDomainMismatch, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_CANNOT_SIGN_REQUEST:
return DemoSetupController::DemoSetupError(
ErrorCode::kSigningError, RecoveryMethod::kPowerwash, debug_message);
case policy::DM_STATUS_SERVICE_POLICY_NOT_FOUND:
return DemoSetupController::DemoSetupError(
ErrorCode::kPolicyNotFound, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_ARC_DISABLED:
return DemoSetupController::DemoSetupError(
ErrorCode::kArcError, RecoveryMethod::kUnknown, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported client status";
return DemoSetupController::DemoSetupError(
ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown, debug_message);
}
DemoSetupController::DemoSetupError CreateFromLockStatus(
InstallAttributes::LockResult status,
const std::string& debug_message) {
switch (status) {
case InstallAttributes::LOCK_SUCCESS:
case InstallAttributes::LOCK_NOT_READY:
return DemoSetupController::DemoSetupError(
ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown, debug_message);
case InstallAttributes::LOCK_TIMEOUT:
return DemoSetupController::DemoSetupError(
ErrorCode::kLockTimeout, RecoveryMethod::kReboot, debug_message);
case InstallAttributes::LOCK_BACKEND_INVALID:
case InstallAttributes::LOCK_SET_ERROR:
case InstallAttributes::LOCK_FINALIZE_ERROR:
case InstallAttributes::LOCK_READBACK_ERROR:
return DemoSetupController::DemoSetupError(
ErrorCode::kLockError, RecoveryMethod::kPowerwash, debug_message);
case InstallAttributes::LOCK_ALREADY_LOCKED:
case InstallAttributes::LOCK_WRONG_DOMAIN:
case InstallAttributes::LOCK_WRONG_MODE:
return DemoSetupController::DemoSetupError(
ErrorCode::kAlreadyLocked, RecoveryMethod::kPowerwash, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported lock status";
return DemoSetupController::DemoSetupError(
ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown, debug_message);
}
} // namespace
// static
constexpr char DemoSetupController::kDemoModeDomain[];
// static
DemoSetupController::DemoSetupError
DemoSetupController::DemoSetupError::CreateFromEnrollmentStatus(
const policy::EnrollmentStatus& status) {
const std::string debug_message = base::StringPrintf(
"EnrollmentError: (status: %d, client_status: %d, store_status: %d, "
"validation_status: %d, lock_status: %d)",
status.status(), status.client_status(), status.store_status(),
status.validation_status(), status.lock_status());
switch (status.status()) {
case policy::EnrollmentStatus::SUCCESS:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::NO_STATE_KEYS:
return DemoSetupError(ErrorCode::kNoStateKeys, RecoveryMethod::kReboot,
debug_message);
case policy::EnrollmentStatus::REGISTRATION_FAILED:
return CreateFromClientStatus(status.client_status(), debug_message);
case policy::EnrollmentStatus::ROBOT_AUTH_FETCH_FAILED:
case policy::EnrollmentStatus::ROBOT_REFRESH_FETCH_FAILED:
return DemoSetupError(ErrorCode::kRobotFetchError,
RecoveryMethod::kCheckNetwork, debug_message);
case policy::EnrollmentStatus::ROBOT_REFRESH_STORE_FAILED:
return DemoSetupError(ErrorCode::kRobotStoreError,
RecoveryMethod::kReboot, debug_message);
case policy::EnrollmentStatus::REGISTRATION_BAD_MODE:
return DemoSetupError(ErrorCode::kBadMode, RecoveryMethod::kRetry,
debug_message);
case policy::EnrollmentStatus::REGISTRATION_CERT_FETCH_FAILED:
return DemoSetupError(ErrorCode::kCertFetchError, RecoveryMethod::kRetry,
debug_message);
case policy::EnrollmentStatus::POLICY_FETCH_FAILED:
return DemoSetupError(ErrorCode::kPolicyFetchError,
RecoveryMethod::kRetry, debug_message);
case policy::EnrollmentStatus::VALIDATION_FAILED:
return DemoSetupError(ErrorCode::kPolicyValidationError,
RecoveryMethod::kRetry, debug_message);
case policy::EnrollmentStatus::LOCK_ERROR:
return CreateFromLockStatus(status.lock_status(), debug_message);
case policy::EnrollmentStatus::STORE_ERROR:
return DemoSetupError(ErrorCode::kOnlineStoreError,
RecoveryMethod::kRetry, debug_message);
case policy::EnrollmentStatus::ATTRIBUTE_UPDATE_FAILED:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::NO_MACHINE_IDENTIFICATION:
return DemoSetupError(ErrorCode::kMachineIdentificationError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::ACTIVE_DIRECTORY_POLICY_FETCH_FAILED:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kReboot, debug_message);
case policy::EnrollmentStatus::DM_TOKEN_STORE_FAILED:
return DemoSetupError(ErrorCode::kDMTokenStoreError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::LICENSE_REQUEST_FAILED:
return DemoSetupError(ErrorCode::kLicenseError, RecoveryMethod::kUnknown,
debug_message);
case policy::EnrollmentStatus::OFFLINE_POLICY_LOAD_FAILED:
case policy::EnrollmentStatus::OFFLINE_POLICY_DECODING_FAILED:
return DemoSetupError(ErrorCode::kOfflinePolicyError,
RecoveryMethod::kOnlineOnly, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported enrollment status";
return DemoSetupError(ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown,
debug_message);
}
// static
DemoSetupController::DemoSetupError
DemoSetupController::DemoSetupError::CreateFromOtherEnrollmentError(
EnterpriseEnrollmentHelper::OtherError error) {
const std::string debug_message =
base::StringPrintf("Other error: %d", error);
switch (error) {
case EnterpriseEnrollmentHelper::OTHER_ERROR_DOMAIN_MISMATCH:
return DemoSetupError(ErrorCode::kAlreadyLocked,
RecoveryMethod::kPowerwash, debug_message);
case EnterpriseEnrollmentHelper::OTHER_ERROR_FATAL:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kUnknown, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported enrollment error";
return DemoSetupError(ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown,
debug_message);
}
// static
DemoSetupController::DemoSetupError
DemoSetupController::DemoSetupError::CreateFromComponentError(
component_updater::CrOSComponentManager::Error error) {
const std::string debug_message =
"Failed to load demo resources CrOS component with error: " +
std::to_string(static_cast<int>(error));
return DemoSetupError(ErrorCode::kOnlineComponentError,
RecoveryMethod::kCheckNetwork, debug_message);
}
DemoSetupController::DemoSetupError::DemoSetupError(
DemoSetupError::ErrorCode error_code,
DemoSetupError::RecoveryMethod recovery_method)
: error_code_(error_code), recovery_method_(recovery_method) {}
DemoSetupController::DemoSetupError::DemoSetupError(
DemoSetupError::ErrorCode error_code,
DemoSetupError::RecoveryMethod recovery_method,
const std::string& debug_message)
: error_code_(error_code),
recovery_method_(recovery_method),
debug_message_(debug_message) {}
DemoSetupController::DemoSetupError::~DemoSetupError() = default;
base::string16 DemoSetupController::DemoSetupError::GetLocalizedErrorMessage()
const {
switch (error_code_) {
case ErrorCode::kNoOfflineResources:
return l10n_util::GetStringUTF16(
IDS_DEMO_SETUP_NO_OFFLINE_RESOURCES_ERROR);
case ErrorCode::kOfflinePolicyError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_OFFLINE_POLICY_ERROR);
case ErrorCode::kOfflinePolicyStoreError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_OFFLINE_STORE_ERROR);
case ErrorCode::kOnlineFRECheckRequired:
return l10n_util::GetStringUTF16(
IDS_DEMO_SETUP_OFFLINE_UNAVAILABLE_ERROR);
case ErrorCode::kOnlineComponentError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_COMPONENT_ERROR);
case ErrorCode::kNoStateKeys:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_NO_STATE_KEYS_ERROR);
case ErrorCode::kInvalidRequest:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_INVALID_REQUEST_ERROR);
case ErrorCode::kRequestNetworkError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_NETWORK_ERROR);
case ErrorCode::kTemporaryUnavailable:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_TEMPORARY_ERROR);
case ErrorCode::kResponseError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RESPONSE_ERROR);
case ErrorCode::kResponseDecodingError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RESPONSE_DECODING_ERROR);
case ErrorCode::kDemoAccountError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ACCOUNT_ERROR);
case ErrorCode::kDeviceNotFound:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DEVICE_NOT_FOUND_ERROR);
case ErrorCode::kInvalidDMToken:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_INVALID_DM_TOKEN_ERROR);
case ErrorCode::kInvalidSerialNumber:
return l10n_util::GetStringUTF16(
IDS_DEMO_SETUP_INVALID_SERIAL_NUMBER_ERROR);
case ErrorCode::kDeviceIdError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DEVICE_ID_ERROR);
case ErrorCode::kLicenseError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_LICENSE_ERROR);
case ErrorCode::kDeviceDeprovisioned:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DEPROVISIONED_ERROR);
case ErrorCode::kDomainMismatch:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DOMAIN_MISMATCH_ERROR);
case ErrorCode::kSigningError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_SIGNING_ERROR);
case ErrorCode::kPolicyNotFound:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_POLICY_NOT_FOUND_ERROR);
case ErrorCode::kArcError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ARC_ERROR);
case ErrorCode::kRobotFetchError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ROBOT_ERROR);
case ErrorCode::kRobotStoreError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ROBOT_STORE_ERROR);
case ErrorCode::kBadMode:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_BAD_MODE_ERROR);
case ErrorCode::kCertFetchError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_CERT_FETCH_ERROR);
case ErrorCode::kPolicyFetchError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_POLICY_FETCH_ERROR);
case ErrorCode::kPolicyValidationError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_POLICY_VALIDATION_ERROR);
case ErrorCode::kLockTimeout:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_LOCK_TIMEOUT_ERROR);
case ErrorCode::kLockError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_LOCK_ERROR);
case ErrorCode::kAlreadyLocked:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ALREADY_LOCKED_ERROR);
case ErrorCode::kOnlineStoreError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ONLINE_STORE_ERROR);
case ErrorCode::kMachineIdentificationError:
return l10n_util::GetStringUTF16(
IDS_DEMO_SETUP_NO_MACHINE_IDENTIFICATION_ERROR);
case ErrorCode::kDMTokenStoreError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DM_TOKEN_STORE_ERROR);
case ErrorCode::kUnexpectedError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_UNEXPECTED_ERROR);
}
NOTREACHED() << "No localized error message available for demo setup error.";
return base::string16();
}
base::string16
DemoSetupController::DemoSetupError::GetLocalizedRecoveryMessage() const {
switch (recovery_method_) {
case RecoveryMethod::kRetry:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_RETRY);
case RecoveryMethod::kReboot:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_REBOOT);
case RecoveryMethod::kPowerwash:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_POWERWASH);
case RecoveryMethod::kCheckNetwork:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_CHECK_NETWORK);
case RecoveryMethod::kOnlineOnly:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_OFFLINE_FATAL);
case RecoveryMethod::kUnknown:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_FATAL);
}
NOTREACHED()
<< "No localized error message available for demo setup recovery method.";
return base::string16();
}
std::string DemoSetupController::DemoSetupError::GetDebugDescription() const {
return base::StringPrintf("DemoSetupError (code: %d, recovery: %d) : %s",
error_code_, recovery_method_,
debug_message_.c_str());
}
void DemoSetupController::RegisterLocalStatePrefs(
PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(
prefs::kDemoModeConfig,
static_cast<int>(DemoSession::DemoModeConfig::kNone));
}
// static
void DemoSetupController::ClearDemoRequisition(
policy::DeviceCloudPolicyManagerChromeOS* policy_manager) {
if (policy_manager->GetDeviceRequisition() == kDemoRequisition) {
policy_manager->SetDeviceRequisition(std::string());
}
}
// static
bool DemoSetupController::IsDemoModeAllowed() {
// Demo mode is only allowed on devices that support ARC++.
return arc::IsArcAvailable();
}
// static
bool DemoSetupController::IsOfflineDemoModeAllowed() {
// Offline demo mode can be only enabled when demo mode feature is enabled.
return IsDemoModeAllowed() &&
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableOfflineDemoMode);
}
// static
bool DemoSetupController::IsOobeDemoSetupFlowInProgress() {
const WizardController* const wizard_controller =
WizardController::default_controller();
return wizard_controller &&
wizard_controller->demo_setup_controller() != nullptr;
}
DemoSetupController::DemoSetupController() : weak_ptr_factory_(this) {}
DemoSetupController::~DemoSetupController() {
if (device_local_account_policy_store_)
device_local_account_policy_store_->RemoveObserver(this);
}
bool DemoSetupController::IsOfflineEnrollment() const {
return demo_config_ == DemoSession::DemoModeConfig::kOffline;
}
void DemoSetupController::Enroll(OnSetupSuccess on_setup_success,
OnSetupError on_setup_error) {
DCHECK_NE(demo_config_, DemoSession::DemoModeConfig::kNone)
<< "Demo config needs to be explicitly set before calling Enroll()";
DCHECK(!enrollment_helper_);
on_setup_success_ = std::move(on_setup_success);
on_setup_error_ = std::move(on_setup_error);
VLOG(1) << "Starting demo setup "
<< DemoSession::DemoConfigToString(demo_config_);
switch (demo_config_) {
case DemoSession::DemoModeConfig::kOnline:
LoadDemoResourcesCrOSComponent();
return;
case DemoSession::DemoModeConfig::kOffline: {
const base::FilePath offline_data_dir =
policy_dir_for_tests_.empty() ? base::FilePath(kOfflineDemoModeDir)
: policy_dir_for_tests_;
EnrollOffline(offline_data_dir);
return;
}
case DemoSession::DemoModeConfig::kNone:
NOTREACHED() << "No valid demo mode config specified";
}
}
void DemoSetupController::LoadDemoResourcesCrOSComponent() {
VLOG(1) << "Loading demo resources component";
component_updater::CrOSComponentManager* cros_component_manager =
g_browser_process->platform_part()->cros_component_manager();
// In tests, use the desired error code.
if (!cros_component_manager || DBusThreadManager::Get()->IsUsingFakes()) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&DemoSetupController::OnDemoResourcesCrOSComponentLoaded,
weak_ptr_factory_.GetWeakPtr(),
component_error_for_tests_, base::FilePath()));
return;
}
cros_component_manager->Load(
DemoResources::kDemoModeResourcesComponentName,
component_updater::CrOSComponentManager::MountPolicy::kMount,
component_updater::CrOSComponentManager::UpdatePolicy::kDontForce,
base::BindOnce(&DemoSetupController::OnDemoResourcesCrOSComponentLoaded,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoSetupController::OnDemoResourcesCrOSComponentLoaded(
component_updater::CrOSComponentManager::Error error,
const base::FilePath& path) {
DCHECK_EQ(demo_config_, DemoSession::DemoModeConfig::kOnline);
if (error != component_updater::CrOSComponentManager::Error::NONE) {
SetupFailed(DemoSetupError::CreateFromComponentError(error));
return;
}
VLOG(1) << "Starting online enrollment";
policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetDeviceCloudPolicyManager();
DCHECK(policy_manager->GetDeviceRequisition().empty());
policy_manager->SetDeviceRequisition(kDemoRequisition);
policy::EnrollmentConfig config;
config.mode = policy::EnrollmentConfig::MODE_ATTESTATION;
config.management_domain = DemoSetupController::kDemoModeDomain;
enrollment_helper_ = EnterpriseEnrollmentHelper::Create(
this, nullptr, config, DemoSetupController::kDemoModeDomain);
enrollment_helper_->EnrollUsingAttestation();
}
void DemoSetupController::EnrollOffline(const base::FilePath& policy_dir) {
DCHECK_EQ(demo_config_, DemoSession::DemoModeConfig::kOffline);
DCHECK(policy_dir_.empty());
policy_dir_ = policy_dir;
if (IsOnlineFreCheckRequired()) {
SetupFailed(
DemoSetupError(DemoSetupError::ErrorCode::kOnlineFRECheckRequired,
DemoSetupError::RecoveryMethod::kOnlineOnly,
"Cannot do offline demo mode setup, because online "
"FRE check is required."));
return;
}
VLOG(1) << "Checking if offline policy exists";
std::string* message = new std::string();
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&CheckOfflinePolicyFilesExist, policy_dir_, message),
base::BindOnce(&DemoSetupController::OnOfflinePolicyFilesExisted,
weak_ptr_factory_.GetWeakPtr(), base::Owned(message)));
}
void DemoSetupController::OnOfflinePolicyFilesExisted(std::string* message,
bool ok) {
DCHECK_EQ(demo_config_, DemoSession::DemoModeConfig::kOffline);
DCHECK(!policy_dir_.empty());
if (!ok) {
SetupFailed(DemoSetupError(DemoSetupError::ErrorCode::kNoOfflineResources,
DemoSetupError::RecoveryMethod::kOnlineOnly,
*message));
return;
}
VLOG(1) << "Starting offline enrollment";
policy::EnrollmentConfig config;
config.mode = policy::EnrollmentConfig::MODE_OFFLINE_DEMO;
config.management_domain = DemoSetupController::kDemoModeDomain;
config.offline_policy_path =
policy_dir_.AppendASCII(kOfflineDevicePolicyFileName);
enrollment_helper_ = EnterpriseEnrollmentHelper::Create(
this, nullptr /* ad_join_delegate */, config,
DemoSetupController::kDemoModeDomain);
enrollment_helper_->EnrollForOfflineDemo();
}
void DemoSetupController::OnAuthError(const GoogleServiceAuthError& error) {
NOTREACHED();
}
void DemoSetupController::OnEnrollmentError(policy::EnrollmentStatus status) {
SetupFailed(DemoSetupError::CreateFromEnrollmentStatus(status));
}
void DemoSetupController::OnOtherError(
EnterpriseEnrollmentHelper::OtherError error) {
SetupFailed(DemoSetupError::CreateFromOtherEnrollmentError(error));
}
void DemoSetupController::OnDeviceEnrolled(
const std::string& additional_token) {
DCHECK_NE(demo_config_, DemoSession::DemoModeConfig::kNone);
// Try to load the policy for the device local account.
if (demo_config_ == DemoSession::DemoModeConfig::kOffline) {
VLOG(1) << "Loading offline policy";
DCHECK(!policy_dir_.empty());
const base::FilePath file_path =
policy_dir_.AppendASCII(kOfflineDeviceLocalAccountPolicyFileName);
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&ReadFileToOptionalString, file_path),
base::BindOnce(&DemoSetupController::OnDeviceLocalAccountPolicyLoaded,
weak_ptr_factory_.GetWeakPtr()));
return;
}
VLOG(1) << "Marking device registered";
StartupUtils::MarkDeviceRegistered(
base::BindOnce(&DemoSetupController::OnDeviceRegistered,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoSetupController::OnMultipleLicensesAvailable(
const EnrollmentLicenseMap& licenses) {
NOTREACHED();
}
void DemoSetupController::OnDeviceAttributeUploadCompleted(bool success) {
NOTREACHED();
}
void DemoSetupController::OnDeviceAttributeUpdatePermission(bool granted) {
NOTREACHED();
}
void DemoSetupController::OnRestoreAfterRollbackCompleted() {
NOTREACHED();
}
void DemoSetupController::SetCrOSComponentLoadErrorForTest(
component_updater::CrOSComponentManager::Error error) {
component_error_for_tests_ = error;
}
void DemoSetupController::SetDeviceLocalAccountPolicyStoreForTest(
policy::CloudPolicyStore* store) {
device_local_account_policy_store_ = store;
}
void DemoSetupController::SetOfflineDataDirForTest(
const base::FilePath& offline_dir) {
policy_dir_for_tests_ = offline_dir;
}
void DemoSetupController::OnDeviceLocalAccountPolicyLoaded(
base::Optional<std::string> blob) {
if (!blob.has_value()) {
// This is very unlikely to happen since the file existence is already
// checked as CheckOfflinePolicyFilesExist.
SetupFailed(
DemoSetupError(DemoSetupError::ErrorCode::kOfflinePolicyError,
DemoSetupError::RecoveryMethod::kPowerwash,
"Policy file for the device local account not found"));
return;
}
enterprise_management::PolicyFetchResponse policy;
if (!policy.ParseFromString(blob.value())) {
SetupFailed(DemoSetupError(DemoSetupError::ErrorCode::kOfflinePolicyError,
DemoSetupError::RecoveryMethod::kPowerwash,
"Error parsing local account policy blob."));
return;
}
// Extract the account_id from the policy data.
enterprise_management::PolicyData policy_data;
if (policy.policy_data().empty() ||
!policy_data.ParseFromString(policy.policy_data())) {
SetupFailed(DemoSetupError(DemoSetupError::ErrorCode::kOfflinePolicyError,
DemoSetupError::RecoveryMethod::kPowerwash,
"Error parsing local account policy data."));
return;
}
VLOG(1) << "Storing offline policy";
// On the unittest, the device_local_account_policy_store_ is already
// initialized. Otherwise attempts to get the store.
if (!device_local_account_policy_store_) {
device_local_account_policy_store_ =
GetDeviceLocalAccountPolicyStore(policy_data.username());
}
if (!device_local_account_policy_store_) {
SetupFailed(
DemoSetupError(DemoSetupError::ErrorCode::kOfflinePolicyStoreError,
DemoSetupError::RecoveryMethod::kPowerwash,
"Can't find the store for the local account policy."));
return;
}
device_local_account_policy_store_->AddObserver(this);
device_local_account_policy_store_->Store(policy);
}
void DemoSetupController::OnDeviceRegistered() {
VLOG(1) << "Demo mode setup finished successfully.";
PrefService* prefs = g_browser_process->local_state();
prefs->SetInteger(prefs::kDemoModeConfig, static_cast<int>(demo_config_));
prefs->CommitPendingWrite();
Reset();
if (!on_setup_success_.is_null())
std::move(on_setup_success_).Run();
}
void DemoSetupController::SetupFailed(const DemoSetupError& error) {
Reset();
LOG(ERROR) << error.GetDebugDescription();
if (!on_setup_error_.is_null())
std::move(on_setup_error_).Run(error);
}
void DemoSetupController::Reset() {
DCHECK_NE(demo_config_, DemoSession::DemoModeConfig::kNone);
DCHECK_NE(demo_config_ == DemoSession::DemoModeConfig::kOffline,
policy_dir_.empty());
// |demo_config_| is not reset here, because it is needed for retrying setup.
enrollment_helper_.reset();
policy_dir_.clear();
if (device_local_account_policy_store_) {
device_local_account_policy_store_->RemoveObserver(this);
device_local_account_policy_store_ = nullptr;
}
policy::DeviceCloudPolicyManagerChromeOS* policy_manager =
g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetDeviceCloudPolicyManager();
ClearDemoRequisition(policy_manager);
}
void DemoSetupController::OnStoreLoaded(policy::CloudPolicyStore* store) {
DCHECK_EQ(store, device_local_account_policy_store_);
VLOG(1) << "Marking device registered";
StartupUtils::MarkDeviceRegistered(
base::BindOnce(&DemoSetupController::OnDeviceRegistered,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoSetupController::OnStoreError(policy::CloudPolicyStore* store) {
DCHECK_EQ(store, device_local_account_policy_store_);
SetupFailed(
DemoSetupError(DemoSetupError::ErrorCode::kOfflinePolicyStoreError,
DemoSetupError::RecoveryMethod::kPowerwash,
"Failed to store the local account policy"));
}
} // namespace chromeos