chromium/chrome/browser/ash/bruschetta/bruschetta_util.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/bruschetta/bruschetta_util.h"

#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ash/bruschetta/bruschetta_pref_names.h"
#include "chrome/browser/ash/bruschetta/bruschetta_service.h"
#include "chrome/browser/ash/guest_os/guest_id.h"
#include "chrome/browser/ash/guest_os/guest_os_pref_names.h"
#include "chrome/browser/ash/guest_os/virtual_machines/virtual_machines_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/grit/generated_resources.h"
#include "components/prefs/pref_service.h"
#include "ui/base/l10n/l10n_util.h"

namespace bruschetta {

namespace {
std::optional<const base::Value::Dict*> GetConfigWithEnabledLevel(
    const Profile* profile,
    const std::string& config_id,
    prefs::PolicyEnabledState enabled_level) {
  // If virtual machines are disabled, we should treat every policy as
  // BLOCKED. If the caller is looking for an enabled level of RUN_ALLOWED
  // or higher we should return nothing, but it can still be useful in
  // some places to retrieve a config even if it's currently BLOCKED e.g. for
  // display names.
  if (!virtual_machines::AreVirtualMachinesAllowedByPolicy() &&
      enabled_level > prefs::PolicyEnabledState::BLOCKED) {
    return std::nullopt;
  }

  const auto* config_ptr = profile->GetPrefs()
                               ->GetDict(prefs::kBruschettaVMConfiguration)
                               .FindDict(config_id);
  if (!config_ptr || config_ptr->FindInt(prefs::kPolicyEnabledKey) <
                         static_cast<int>(enabled_level)) {
    return std::nullopt;
  }

  return config_ptr;
}
}  // namespace

const char kToolsDlc[] = "termina-tools-dlc";
const char kUefiDlc[] = "edk2-ovmf-dlc";

const char kBruschettaVmName[] = "bru";

const char* BruschettaResultString(const BruschettaResult res) {
#define ENTRY(name)            \
  case BruschettaResult::name: \
    return #name
  switch (res) {
    ENTRY(kUnknown);
    ENTRY(kSuccess);
    ENTRY(kDlcInstallError);
    ENTRY(kStartVmFailed);
    ENTRY(kTimeout);
    ENTRY(kForbiddenByPolicy);
    ENTRY(kConciergeUnavailable);
  }
#undef ENTRY
  return "unknown code";
}

guest_os::GuestId GetBruschettaAlphaId() {
  return MakeBruschettaId(kBruschettaVmName);
}

guest_os::GuestId MakeBruschettaId(std::string vm_name) {
  return guest_os::GuestId{guest_os::VmType::BRUSCHETTA, std::move(vm_name),
                           "penguin"};
}

std::optional<const base::Value::Dict*> GetRunnableConfig(
    const Profile* profile,
    const std::string& config_id) {
  return GetConfigWithEnabledLevel(profile, config_id,
                                   prefs::PolicyEnabledState::RUN_ALLOWED);
}

base::FilePath BruschettaChromeOSBaseDirectory() {
  return base::FilePath("/mnt/shared");
}

std::optional<const base::Value::Dict*> GetInstallableConfig(
    const Profile* profile,
    const std::string& config_id) {
  return GetConfigWithEnabledLevel(profile, config_id,
                                   prefs::PolicyEnabledState::INSTALL_ALLOWED);
}

bool HasInstallableConfig(const Profile* profile,
                          const std::string& config_id) {
  return GetInstallableConfig(profile, config_id).has_value();
}

base::flat_map<std::string, base::Value::Dict> GetInstallableConfigs(
    const Profile* profile) {
  base::flat_map<std::string, base::Value::Dict> ret;
  for (auto it :
       profile->GetPrefs()->GetDict(prefs::kBruschettaVMConfiguration)) {
    if (HasInstallableConfig(profile, it.first)) {
      ret.emplace(it.first, it.second.GetDict().Clone());
    }
  }

  return ret;
}

void SortInstallableConfigs(std::vector<InstallableConfig>* configs) {
  auto GetDisplayOrder = [](const InstallableConfig& c) -> int {
    return c.second.FindInt(bruschetta::prefs::kPolicyDisplayOrderKey)
        .value_or(0);
  };
  std::sort(configs->begin(), configs->end(),
            [&GetDisplayOrder](const InstallableConfig& a,
                               const InstallableConfig& b) {
              return GetDisplayOrder(a) < GetDisplayOrder(b);
            });
}

bool IsInstalled(Profile* profile, const guest_os::GuestId& guest_id) {
  const base::Value* value = guest_os::GetContainerPrefValue(
      profile, guest_id, guest_os::prefs::kVmNameKey);
  return value != nullptr;
}

std::optional<RunningVmPolicy> GetLaunchPolicyForConfig(Profile* profile,
                                                        std::string config_id) {
  auto config_option = GetRunnableConfig(profile, config_id);
  if (!config_option.has_value()) {
    return std::nullopt;
  }
  const auto* config = *config_option;

  RunningVmPolicy ret = {.vtpm_enabled =
                             config->FindDict(prefs::kPolicyVTPMKey)
                                 ->FindBool(prefs::kPolicyVTPMEnabledKey)
                                 .value()};

  return ret;
}

std::string GetVmUsername(const Profile* profile) {
  std::string username = profile->GetProfileUserName();
  // Return the part before the '@' if this is an email. Since find returns
  // std::string::npos if it can't find the token this will return the full
  // username in that case.
  return username.substr(0, username.find("@"));
}

std::optional<const base::Value::Dict*> GetConfigForGuest(
    Profile* profile,
    const guest_os::GuestId& guest_id,
    prefs::PolicyEnabledState enabled_level) {
  const auto* config_id_val = guest_os::GetContainerPrefValue(
      profile, guest_id, guest_os::prefs::kBruschettaConfigId);
  if (!config_id_val) {
    return std::nullopt;
  }

  const auto& config_id = config_id_val->GetString();

  return GetConfigWithEnabledLevel(profile, config_id, enabled_level);
}

std::u16string GetOverallVmName(Profile* profile) {
  const std::u16string fallback_name =
      l10n_util::GetStringUTF16(IDS_BRUSCHETTA_NAME);
  if (!profile) {
    // If no profile is present (e.g. some tests), we can't access the policy.
    return fallback_name;
  }

  // First see if the name is explicitly set in policy.
  const auto& installer_config =
      profile->GetPrefs()->GetDict(prefs::kBruschettaInstallerConfiguration);
  const auto* display_name =
      installer_config.FindString(prefs::kPolicyDisplayNameKey);
  if (display_name) {
    return base::UTF8ToUTF16(*display_name);
  }

  // If not, pick the name of the first VM in configuration.
  std::vector<bruschetta::InstallableConfig> configs =
      bruschetta::GetInstallableConfigs(profile).extract();
  if (!configs.empty()) {
    SortInstallableConfigs(&configs);
    const base::Value::Dict& first_vm = configs[0].second;
    const auto* name = first_vm.FindString(prefs::kPolicyNameKey);
    if (name) {
      return base::UTF8ToUTF16(*name);
    }
    // Config exists but no name, use the key as its name.
    return base::UTF8ToUTF16(configs[0].first);
  }
  return fallback_name;
}

GURL GetLearnMoreUrl(Profile* profile) {
  // First see if the name is explicitly set in policy.
  const auto& installer_config =
      profile->GetPrefs()->GetDict(prefs::kBruschettaInstallerConfiguration);
  const auto* url = installer_config.FindString(prefs::kPolicyLearnMoreUrlKey);
  if (url) {
    return GURL(*url);
  }
  return GURL();
}

std::string GetDisplayName(Profile* profile, guest_os::GuestId guest) {
  auto config =
      GetConfigForGuest(profile, guest, prefs::PolicyEnabledState::BLOCKED);
  const std::string* name = nullptr;
  if (config.has_value()) {
    name = config.value()->FindString(prefs::kPolicyNameKey);
  }
  if (name) {
    return *name;
  }
  // If the config doesn't exist, the terminal will default to
  // <vm_name>:<container_name>, but container_name isn't meaningful for us
  // so just use the vm_name instead.
  return guest.vm_name;
}

bool IsBruschettaRunning(Profile* profile) {
  auto* service = bruschetta::BruschettaService::GetForProfile(profile);
  return service && service->IsVmRunning(kBruschettaVmName);
}

std::string GetBruschettaDisplayName(Profile* profile) {
  return GetDisplayName(profile, GetBruschettaAlphaId());
}

}  // namespace bruschetta