chromium/chromeos/ash/components/standalone_browser/lacros_availability.cc

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

#include "chromeos/ash/components/standalone_browser/lacros_availability.h"

#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/containers/fixed_flat_map.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/user_manager/user.h"
#include "google_apis/gaia/gaia_auth_util.h"

namespace ash::standalone_browser {
namespace {

// The conversion map for LacrosAvailability policy data. The values must match
// the ones from LacrosAvailability.yaml.
// TODO(crbug.com/40269372): Remove the side_by_side and lacros_primary values
// from the policy.
constexpr auto kLacrosAvailabilityMap =
    base::MakeFixedFlatMap<std::string_view, LacrosAvailability>({
        {"user_choice", LacrosAvailability::kUserChoice},
        {"lacros_disallowed", LacrosAvailability::kLacrosDisallowed},
        {"side_by_side", LacrosAvailability::kLacrosDisallowed},
        {"lacros_primary", LacrosAvailability::kLacrosDisallowed},
        {"lacros_only", LacrosAvailability::kLacrosOnly},
    });

}  // namespace

BASE_FEATURE(kLacrosGooglePolicyRollout,
             "LacrosGooglePolicyRollout",
             base::FEATURE_ENABLED_BY_DEFAULT);

std::optional<LacrosAvailability> ParseLacrosAvailability(
    std::string_view value) {
  auto it = kLacrosAvailabilityMap.find(value);
  if (it != kLacrosAvailabilityMap.end()) {
    return it->second;
  }

  LOG(ERROR) << "Unknown LacrosAvailability policy value is passed: " << value;
  return std::nullopt;
}

std::string_view GetLacrosAvailabilityPolicyName(LacrosAvailability value) {
  for (const auto& entry : kLacrosAvailabilityMap) {
    if (entry.second == value) {
      return entry.first;
    }
  }

  NOTREACHED_IN_MIGRATION();
  return std::string_view();
}

bool IsGoogleInternal(const user_manager::User* user) {
  if (!user) {
    return false;
  }

  const std::string_view email = user->GetAccountId().GetUserEmail();
  return gaia::IsGoogleInternalAccountEmail(email) ||
         gaia::IsGoogleRobotAccountEmail(email) ||
         gaia::ExtractDomainName(gaia::SanitizeEmail(email)) ==
             "managedchrome.com";
}

LacrosAvailability DetermineLacrosAvailabilityFromPolicyValue(
    const user_manager::User* user,
    std::string_view policy_value) {
  // Users can set this switch in chrome://flags to disable the effect of the
  // lacros-availability policy. This should only be allowed for Googlers.
  // Note: Flag actively used by CfM to bypass LaCrOS Availability Policy.
  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(ash::switches::kLacrosAvailabilityIgnore) &&
      IsGoogleInternal(user)) {
    return LacrosAvailability::kUserChoice;
  }

  if (policy_value.empty()) {
    // Some tests call IsLacrosAllowedToBeEnabled but don't have the value set.
    return LacrosAvailability::kUserChoice;
  }

  auto result = ParseLacrosAvailability(policy_value);
  if (!result.has_value()) {
    return LacrosAvailability::kUserChoice;
  }

  if (IsGoogleInternal(user) &&
      !base::FeatureList::IsEnabled(kLacrosGooglePolicyRollout) &&
      result != LacrosAvailability::kLacrosDisallowed) {
    return LacrosAvailability::kUserChoice;
  }

  return result.value();
}

LacrosAvailability GetLacrosAvailability(const user_manager::User* user,
                                         const policy::PolicyMap& policy_map) {
  const base::Value* value = policy_map.GetValue(
      policy::key::kLacrosAvailability, base::Value::Type::STRING);
  return DetermineLacrosAvailabilityFromPolicyValue(
      user, value ? value->GetString() : std::string_view());
}

}  // namespace ash::standalone_browser