// Copyright 2021 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/policy/handlers/device_name_policy_handler_impl.h"
#include <string_view>
#include "ash/constants/ash_features.h"
#include "base/functional/bind.h"
#include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h"
#include "chrome/browser/ash/policy/handlers/device_name_policy_handler_name_generator.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chromeos/ash/components/install_attributes/install_attributes.h"
#include "chromeos/ash/components/network/device_state.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/settings/cros_settings_names.h"
#include "chromeos/ash/components/settings/cros_settings_provider.h"
namespace policy {
namespace {
// By default, device name policy should be kPolicyHostnameNotConfigurable for
// managed devices and kNoPolicy for unmanaged devices.
DeviceNamePolicyHandler::DeviceNamePolicy ComputeInitialPolicy() {
if (ash::InstallAttributes::Get()->IsEnterpriseManaged()) {
// We assume that the device name is not configurable unless/until we know
// about any policies that are set.
return DeviceNamePolicyHandler::DeviceNamePolicy::
kPolicyHostnameNotConfigurable;
}
return DeviceNamePolicyHandler::DeviceNamePolicy::kNoPolicy;
}
} // namespace
DeviceNamePolicyHandlerImpl::DeviceNamePolicyHandlerImpl(
ash::CrosSettings* cros_settings)
: DeviceNamePolicyHandlerImpl(
cros_settings,
ash::system::StatisticsProvider::GetInstance(),
ash::NetworkHandler::Get()->network_state_handler()) {}
DeviceNamePolicyHandlerImpl::DeviceNamePolicyHandlerImpl(
ash::CrosSettings* cros_settings,
ash::system::StatisticsProvider* statistics_provider,
ash::NetworkStateHandler* handler)
: cros_settings_(cros_settings),
statistics_provider_(statistics_provider),
handler_(handler),
device_name_policy_(ComputeInitialPolicy()) {
template_policy_subscription_ = cros_settings_->AddSettingsObserver(
ash::kDeviceHostnameTemplate,
base::BindRepeating(
&DeviceNamePolicyHandlerImpl::OnDeviceHostnamePropertyChanged,
weak_factory_.GetWeakPtr()));
configurable_policy_subscription_ = cros_settings_->AddSettingsObserver(
ash::kDeviceHostnameUserConfigurable,
base::BindRepeating(
&DeviceNamePolicyHandlerImpl::OnDeviceHostnamePropertyChanged,
weak_factory_.GetWeakPtr()));
network_state_handler_observer_.Observe(
ash::NetworkHandler::Get()->network_state_handler());
// Fire it once so we're sure we get an invocation on startup.
OnDeviceHostnamePropertyChanged();
}
DeviceNamePolicyHandlerImpl::~DeviceNamePolicyHandlerImpl() = default;
DeviceNamePolicyHandler::DeviceNamePolicy
DeviceNamePolicyHandlerImpl::GetDeviceNamePolicy() const {
return device_name_policy_;
}
std::optional<std::string>
DeviceNamePolicyHandlerImpl::GetHostnameChosenByAdministrator() const {
if (GetDeviceNamePolicy() == DeviceNamePolicy::kPolicyHostnameChosenByAdmin) {
return hostname_;
}
return std::nullopt;
}
void DeviceNamePolicyHandlerImpl::DefaultNetworkChanged(
const ash::NetworkState* network) {
OnDeviceHostnamePropertyChanged();
}
void DeviceNamePolicyHandlerImpl::OnShuttingDown() {
network_state_handler_observer_.Reset();
}
void DeviceNamePolicyHandlerImpl::OnDeviceHostnamePropertyChanged() {
ash::CrosSettingsProvider::TrustedStatus status =
cros_settings_->PrepareTrustedValues(base::BindOnce(
&DeviceNamePolicyHandlerImpl::OnDeviceHostnamePropertyChanged,
weak_factory_.GetWeakPtr()));
if (status != ash::CrosSettingsProvider::TRUSTED)
return;
// Continue when machine statistics are loaded, to avoid blocking.
statistics_provider_->ScheduleOnMachineStatisticsLoaded(base::BindOnce(
&DeviceNamePolicyHandlerImpl::
OnDeviceHostnamePropertyChangedAndMachineStatisticsLoaded,
weak_factory_.GetWeakPtr()));
}
void DeviceNamePolicyHandlerImpl::
OnDeviceHostnamePropertyChangedAndMachineStatisticsLoaded() {
std::string hostname_template;
DeviceNamePolicy policy = ComputePolicy(&hostname_template);
std::string new_hostname;
if (policy == DeviceNamePolicy::kPolicyHostnameChosenByAdmin) {
new_hostname = GenerateHostname(hostname_template);
}
SetDeviceNamePolicy(policy, new_hostname);
}
DeviceNamePolicyHandler::DeviceNamePolicy
DeviceNamePolicyHandlerImpl::ComputePolicy(std::string* hostname_template_out) {
if (cros_settings_->GetString(ash::kDeviceHostnameTemplate,
hostname_template_out)) {
// Do not set an empty hostname (which would overwrite any custom hostname
// set) if DeviceHostnameTemplate is not specified by policy.
// No policy is set for administrator to choose hostname.
return DeviceNamePolicy::kPolicyHostnameChosenByAdmin;
}
bool hostname_user_configurable;
if (ash::features::IsHostnameSettingEnabled() &&
cros_settings_->GetBoolean(ash::kDeviceHostnameUserConfigurable,
&hostname_user_configurable)) {
return hostname_user_configurable
? DeviceNamePolicy::kPolicyHostnameConfigurableByManagedUser
: DeviceNamePolicy::kPolicyHostnameNotConfigurable;
}
// If no policies are set, device name policy should be
// kPolicyHostnameNotConfigurable for managed devices and kNoPolicy for
// unmanaged devices.
if (ash::InstallAttributes::Get()->IsEnterpriseManaged())
return DeviceNamePolicy::kPolicyHostnameNotConfigurable;
return DeviceNamePolicy::kNoPolicy;
}
std::string DeviceNamePolicyHandlerImpl::GenerateHostname(
const std::string& hostname_template) const {
const std::string_view serial =
statistics_provider_->GetMachineID().value_or(std::string_view());
const std::string asset_id = g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetDeviceAssetID();
const std::string machine_name = g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetMachineName();
const std::string location = g_browser_process->platform_part()
->browser_policy_connector_ash()
->GetDeviceAnnotatedLocation();
std::string mac = "MAC_unknown";
const ash::NetworkState* network = handler_->DefaultNetwork();
if (network) {
const ash::DeviceState* device =
handler_->GetDeviceState(network->device_path());
if (device) {
mac = device->mac_address();
base::ReplaceSubstringsAfterOffset(&mac, 0, ":", "");
}
}
return FormatHostname(hostname_template, asset_id, serial, mac, machine_name,
location);
}
void DeviceNamePolicyHandlerImpl::SetDeviceNamePolicy(
DeviceNamePolicy policy,
const std::string& new_hostname) {
if (device_name_policy_ == policy && hostname_ == new_hostname)
return;
// If the hostname has changed, set it using NetworkStateHandler. Note that
// this process is skipped when the hostname settings flag is enabled since
// this is handled elsewhere. See https://crbug.com/126802.
if (!ash::features::IsHostnameSettingEnabled() &&
policy == DeviceNamePolicy::kPolicyHostnameChosenByAdmin &&
hostname_ != new_hostname) {
handler_->SetHostname(new_hostname);
}
device_name_policy_ = policy;
hostname_ = new_hostname;
NotifyHostnamePolicyChanged();
}
std::ostream& operator<<(
std::ostream& stream,
const DeviceNamePolicyHandlerImpl::DeviceNamePolicy& state) {
switch (state) {
case DeviceNamePolicyHandlerImpl::DeviceNamePolicy::kNoPolicy:
stream << "[No policy]";
break;
case DeviceNamePolicyHandlerImpl::DeviceNamePolicy::
kPolicyHostnameChosenByAdmin:
stream << "[Admin chooses hostname template]";
break;
case DeviceNamePolicyHandlerImpl::DeviceNamePolicy::
kPolicyHostnameConfigurableByManagedUser:
stream << "[Managed user can choose hostname]";
break;
case DeviceNamePolicyHandlerImpl::DeviceNamePolicy::
kPolicyHostnameNotConfigurable:
stream << "[Managed user cannot choose hostname]";
break;
}
return stream;
}
} // namespace policy