blob: 1386b95e19236c92d8d9edc131f26639ddebdb46 [file] [log] [blame]
// Copyright 2016 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/arc/policy/arc_policy_bridge.h"
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include "arc_policy_util.h"
#include "ash/constants/ash_switches.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/json/json_reader.h"
#include "base/json/json_string_value_serializer.h"
#include "base/logging.h"
#include "base/memory/singleton.h"
#include "base/uuid.h"
#include "base/values.h"
#include "chrome/browser/ash/arc/enterprise/cert_store/cert_store_service.h"
#include "chrome/browser/ash/arc/enterprise/cert_store/cert_store_service_factory.h"
#include "chrome/browser/ash/arc/policy/arc_policy_util.h"
#include "chrome/browser/ash/arc/policy/managed_configuration_variables.h"
#include "chrome/browser/ash/arc/session/arc_session_manager.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/chromeos/platform_keys/extension_key_permissions_service.h"
#include "chrome/browser/policy/developer_tools_policy_handler.h"
#include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/experiences/arc/arc_browser_context_keyed_service_factory_base.h"
#include "chromeos/ash/experiences/arc/arc_prefs.h"
#include "chromeos/ash/experiences/arc/session/arc_bridge_service.h"
#include "chromeos/components/onc/onc_utils.h"
#include "components/onc/onc_constants.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/core/common/policy_namespace.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user.h"
// Enable VLOG level 1.
#undef ENABLED_VLOG_LEVEL
#define ENABLED_VLOG_LEVEL 1
namespace arc {
namespace {
constexpr char kPolicyCompliantJson[] = "{ \"policyCompliant\": true }";
constexpr char kPolicyAppInstallType[] = "installType";
constexpr char kPolicyAppInstallTypeForceInstalled[] = "FORCE_INSTALLED";
constexpr char kPolicyCaCertType[] = "X509";
constexpr char kPolicyRequiredKeyAlias[] = "alias";
constexpr char kPolicyPrivateKeyAlias[] = "privateKeyAlias";
constexpr char kPolicyPrivateKeyPackageNames[] = "packageNames";
constexpr char kPolicyPlayStoreModeSupervised[] = "SUPERVISED";
constexpr char kPolicyPlayStoreModeAllowList[] = "WHITELIST"; // nocheck
// invert_bool_value: If the Chrome policy and the ARC policy with boolean value
// have opposite semantics, set this to true so the bool is inverted before
// being added. Otherwise, set it to false.
void MapBoolToBool(const std::string& arc_policy_name,
const std::string& policy_name,
const policy::PolicyMap& policy_map,
bool invert_bool_value,
base::Value::Dict* filtered_policies) {
if (!policy_map.IsPolicySet(policy_name)) {
return;
}
const base::Value* const policy_value =
policy_map.GetValue(policy_name, base::Value::Type::BOOLEAN);
if (!policy_value) {
NOTREACHED() << "Policy " << policy_name << " is not a boolean.";
}
filtered_policies->Set(arc_policy_name,
policy_value->GetBool() != invert_bool_value);
}
// int_true: value of Chrome OS policy for which arc policy is set to true.
// It is set to false for all other values.
void MapIntToBool(const std::string& arc_policy_name,
const std::string& policy_name,
const policy::PolicyMap& policy_map,
int int_true,
base::Value::Dict* filtered_policies) {
if (!policy_map.IsPolicySet(policy_name)) {
return;
}
const base::Value* const policy_value =
policy_map.GetValue(policy_name, base::Value::Type::INTEGER);
if (!policy_value) {
NOTREACHED() << "Policy " << policy_name << " is not an integer.";
}
filtered_policies->Set(arc_policy_name, policy_value->GetInt() == int_true);
}
// |arc_policy_name| is only set if the |pref_name| pref is managed.
// int_true: value of Chrome OS pref for which arc policy is set to true.
// It is set to false for all other values.
void MapManagedIntPrefToBool(const std::string& arc_policy_name,
const std::string& pref_name,
const PrefService* profile_prefs,
int int_true,
base::Value::Dict* filtered_policies) {
if (!profile_prefs->IsManagedPreference(pref_name)) {
return;
}
filtered_policies->Set(arc_policy_name,
profile_prefs->GetInteger(pref_name) == int_true);
}
// Checks whether |policy_name| is present as an object and has all |fields|,
// Sets |arc_policy_name| to true only if the condition above is satisfied.
void MapObjectToPresenceBool(const std::string& arc_policy_name,
const std::string& policy_name,
const policy::PolicyMap& policy_map,
base::Value::Dict* filtered_policies,
const std::vector<std::string>& fields) {
if (!policy_map.IsPolicySet(policy_name)) {
return;
}
const base::Value* const policy_value =
policy_map.GetValue(policy_name, base::Value::Type::DICT);
if (!policy_value) {
NOTREACHED() << "Policy " << policy_name << " is not an object.";
}
for (const auto& field : fields) {
if (!policy_value->GetDict().contains(field)) {
return;
}
}
filtered_policies->Set(arc_policy_name, true);
}
void AddOncCaCertsToPolicies(const policy::PolicyMap& policy_map,
base::Value::Dict* filtered_policies) {
const base::Value* const policy_value = policy_map.GetValue(
policy::key::kArcCertificatesSyncMode, base::Value::Type::INTEGER);
// Old certs should be uninstalled if the sync is disabled or policy is not
// set.
if (!policy_value ||
policy_value->GetInt() != ArcCertsSyncMode::COPY_CA_CERTS) {
return;
}
if (!policy_map.IsPolicySet(policy::key::kOpenNetworkConfiguration)) {
VLOG(1) << "onc policy is not set.";
return;
}
// Importing CA certificates from device policy is not allowed.
// Import only from user policy.
const base::Value* onc_policy_value = policy_map.GetValue(
policy::key::kOpenNetworkConfiguration, base::Value::Type::STRING);
if (!onc_policy_value) {
LOG(ERROR) << "Value of onc policy has invalid format.";
return;
}
const std::string& onc_blob = onc_policy_value->GetString();
base::Value::List certificates;
{
base::Value::List unused_network_configs;
base::Value::Dict unused_global_network_config;
if (!chromeos::onc::ParseAndValidateOncForImport(
onc_blob, onc::ONCSource::ONC_SOURCE_USER_POLICY,
&unused_network_configs, &unused_global_network_config,
&certificates)) {
LOG(ERROR) << "Value of onc policy has invalid format =" << onc_blob;
}
}
base::Value::List ca_certs;
for (const auto& certificate : certificates) {
if (!certificate.is_dict()) {
DLOG(FATAL) << "Value of a certificate entry is not a dictionary "
<< "value.";
continue;
}
const base::Value::Dict& cert_dict = certificate.GetDict();
const std::string* const cert_type =
cert_dict.FindString(::onc::certificate::kType);
if (!cert_type || *cert_type != ::onc::certificate::kAuthority) {
continue;
}
const base::Value::List* const trust_list =
cert_dict.FindList(::onc::certificate::kTrustBits);
if (!trust_list) {
continue;
}
bool web_trust_flag = false;
for (const auto& list_val : *trust_list) {
if (!list_val.is_string()) {
NOTREACHED();
}
if (list_val.GetString() == ::onc::certificate::kWeb) {
// "Web" implies that the certificate is to be trusted for SSL
// identification.
web_trust_flag = true;
break;
}
}
if (!web_trust_flag) {
continue;
}
const std::string* const x509_data =
cert_dict.FindString(::onc::certificate::kX509);
if (!x509_data) {
continue;
}
base::Value::Dict data;
data.Set(kPolicyCaCertType, *x509_data);
ca_certs.Append(std::move(data));
}
if (!ca_certs.empty()) {
filtered_policies->Set(policy_util::kArcPolicyKeyCredentialsConfigDisabled,
base::Value(true));
}
filtered_policies->Set(policy_util::kArcPolicyKeyCaCerts,
std::move(ca_certs));
}
void AddRequiredKeyPairs(const CertStoreService* cert_store_service,
base::Value::Dict* filtered_policies) {
if (!cert_store_service) {
return;
}
base::Value::List cert_names;
for (const auto& name : cert_store_service->get_required_cert_names()) {
base::Value::Dict value;
value.Set(kPolicyRequiredKeyAlias, name);
cert_names.Append(std::move(value));
}
filtered_policies->Set(policy_util::kArcPolicyKeyRequiredKeyPairs,
std::move(cert_names));
}
bool LooksLikeAndroidPackageName(const std::string& name) {
return name.find(".") != std::string::npos;
}
void AddChoosePrivateKeyRuleToPolicy(
policy::PolicyService* const policy_service,
const CertStoreService* cert_store_service,
base::Value::Dict* filtered_policies) {
if (!cert_store_service) {
return;
}
auto app_ids = chromeos::platform_keys::ExtensionKeyPermissionsService::
GetCorporateKeyUsageAllowedAppIds(policy_service);
base::Value::List arc_app_ids;
for (const auto& app_id : app_ids) {
if (LooksLikeAndroidPackageName(app_id)) {
arc_app_ids.Append(app_id);
}
}
if (arc_app_ids.empty() ||
cert_store_service->get_required_cert_names().empty()) {
return;
}
base::Value::List rules;
for (const auto& name : cert_store_service->get_required_cert_names()) {
base::Value::Dict value;
value.Set(kPolicyPrivateKeyAlias, name);
value.Set(kPolicyPrivateKeyPackageNames, arc_app_ids.Clone());
rules.Append(std::move(value));
}
filtered_policies->Set(policy_util::kArcPolicyKeyPrivateKeySelectionEnabled,
true);
filtered_policies->Set(policy_util::kArcPolicyKeyChoosePrivateKeyRules,
std::move(rules));
}
// Finds managed configurations of applications in |arc_policy| and replace
// string values that refer to template variables.
void ReplaceManagedConfigurationVariables(const Profile* profile,
base::Value::Dict* arc_policy) {
// Replace template variables in application managed configuration.
base::Value::List* applications =
arc_policy->FindList(policy_util::kArcPolicyKeyApplications);
if (applications) {
for (base::Value& entry : *applications) {
base::Value::Dict* config =
entry.GetDict().FindDict(ArcPolicyBridge::kManagedConfiguration);
if (config) {
RecursivelyReplaceManagedConfigurationVariables(profile, *config);
}
}
}
}
void FilterAppsOnReven(base::Value::Dict* arc_policy,
const std::unordered_set<std::string>& allowed_packages) {
base::Value::List* applications =
arc_policy->FindList(policy_util::kArcPolicyKeyApplications);
if (!applications) {
return;
}
applications->EraseIf([&allowed_packages](const base::Value& val) {
const base::Value::Dict& application = val.GetDict();
const std::string* package_name =
application.FindString(ArcPolicyBridge::kPackageName);
if (!package_name) {
return true;
}
bool is_allowed = base::Contains(allowed_packages, *package_name);
bool is_zscaler = package_name->find("zscaler.com.") == 0;
return !is_allowed && !is_zscaler;
});
}
void ConfigureRevenPolicies(base::Value::Dict* arc_policy) {
// The policy value is used to restrict the user from being able to
// toggle between different accounts in ARC++.
arc_policy->Set(policy_util::kArcPolicyKeyPlayStoreMode,
kPolicyPlayStoreModeAllowList);
// Define a set of certified package names for Android VPN apps on Reven.
const std::unordered_set<std::string> allowed_packages = {
"com.paloaltonetworks.globalprotect",
"com.cisco.anyconnect.vpn.android.avf",
"zscaler.com.zschromeosapp",
"com.f5.edge.client_ics",
"com.netskope.netskopeclient",
"com.zimperium.zips",
"com.fortinet.forticlient_vpn",
"com.fortinet.forticlient_fa",
"com.forcepoint.sslvpn"};
FilterAppsOnReven(arc_policy, allowed_packages);
}
base::Value::Dict ParseArcPoliciesToDict(const policy::PolicyMap& policy_map) {
base::Value::Dict filtered_policies;
// It is safe to use `GetValueUnsafe()` because type checking is performed
// before the value is used.
// Parse ArcPolicy as JSON string before adding other policies to the
// dictionary.
const base::Value* const app_policy_value =
policy_map.GetValueUnsafe(policy::key::kArcPolicy);
if (app_policy_value) {
std::optional<base::Value> app_policy_dict;
if (app_policy_value->is_string()) {
app_policy_dict = base::JSONReader::Read(
app_policy_value->GetString(),
base::JSONParserOptions::JSON_ALLOW_TRAILING_COMMAS);
}
if (app_policy_dict.has_value() && app_policy_dict.value().is_dict()) {
// Need a deep copy of all values here instead of doing a swap, because
// JSONReader::Read constructs a dictionary whose StringValues are
// JSONStringValues which are based on std::string_view instead of string.
filtered_policies.Merge(std::move(app_policy_dict.value().GetDict()));
} else {
std::string app_policy_string =
app_policy_value->is_string() ? app_policy_value->GetString() : "";
LOG(ERROR) << "Value of ArcPolicy has invalid format: "
<< app_policy_string;
}
}
return filtered_policies;
}
void MapChromeToArcPolicies(base::Value::Dict& filtered_policies,
const Profile* profile,
const policy::PolicyMap& policy_map) {
const PrefService* profile_prefs = profile->GetPrefs();
// Keep them sorted by the ARC policy names.
MapBoolToBool(policy_util::kArcPolicyKeyCameraDisabled,
policy::key::kVideoCaptureAllowed, policy_map,
/* invert_bool_value */ true, &filtered_policies);
// Use the pref for "debuggingFeaturesDisabled" to avoid duplicating the
// logic of handling DeveloperToolsDisabled / DeveloperToolsAvailability
// policies.
MapManagedIntPrefToBool(
policy_util::kArcPolicyKeyDebuggingFeaturesDisabled,
::prefs::kDevToolsAvailability, profile_prefs,
static_cast<int>(
policy::DeveloperToolsPolicyHandler::Availability::kDisallowed),
&filtered_policies);
MapBoolToBool(policy_util::kArcPolicyKeyPrintingDisabled,
policy::key::kPrintingEnabled, policy_map,
/* invert_bool_value */ true, &filtered_policies);
MapBoolToBool(policy_util::kArcPolicyKeyScreenCaptureDisabled,
policy::key::kDisableScreenshots, policy_map, false,
&filtered_policies);
MapIntToBool(policy_util::kArcPolicyKeyShareLocationDisabled,
policy::key::kDefaultGeolocationSetting, policy_map,
2 /*BlockGeolocation*/, &filtered_policies);
MapBoolToBool(policy_util::kArcPolicyKeyUnmuteMicrophoneDisabled,
policy::key::kAudioCaptureAllowed, policy_map,
/* invert_bool_value */ true, &filtered_policies);
MapObjectToPresenceBool(policy_util::kArcPolicyKeySetWallpaperDisabled,
policy::key::kWallpaperImage, policy_map,
&filtered_policies, {"url", "hash"});
MapBoolToBool(policy_util::kArcPolicyKeyVpnConfigDisabled,
policy::key::kVpnConfigAllowed, policy_map,
/* invert_bool_value */ true, &filtered_policies);
}
void OverrideArcPolicies(base::Value::Dict& filtered_policies,
const policy::PolicyMap& policy_map,
const std::string& guid,
bool is_affiliated,
const Profile* profile) {
MapChromeToArcPolicies(filtered_policies, profile, policy_map);
// If kForceDevToolsAvailable is set, then force debugging features to be
// available for ARC as well. This must be after the initial writing of
// "debuggingFeaturesDisabled".
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kForceDevToolsAvailable)) {
filtered_policies.Set(policy_util::kArcPolicyKeyDebuggingFeaturesDisabled,
false);
}
// Always enable APK Cache for affiliated users, and always disable it for
// not affiliated ones.
filtered_policies.Set(policy_util::kArcPolicyKeyApkCacheEnabled,
is_affiliated);
filtered_policies.Set(policy_util::kArcPolicyKeyGuid, guid);
// Always allow mounting physical media because mounts are controlled
// outside of ARC based on policy in file_manager::VolumeManager. Since this
// Android policy used to be mapped from Chrome-side policy
// policy::key::kExternalStoragePolicy before, we hard-code it to false to
// ensure that the old policy setting does not remain on the ARC side.
// See b/217531658 for details.
filtered_policies.Set(policy_util::kArcPolicyKeyMountPhysicalMediaDisabled,
false);
if (profile->IsChild() &&
ash::ProfileHelper::Get()->IsPrimaryProfile(profile)) {
// Adds "playStoreMode" policy. The policy value is used to restrict the
// user from being able to toggle between different accounts in ARC++.
filtered_policies.Set(policy_util::kArcPolicyKeyPlayStoreMode,
kPolicyPlayStoreModeSupervised);
}
if (ash::switches::IsRevenBranding()) {
ConfigureRevenPolicies(&filtered_policies);
}
}
base::Value::Dict GetFilteredDictPolicies(
policy::PolicyService* const policy_service,
const std::string& guid,
bool is_affiliated,
const CertStoreService* cert_store_service,
const Profile* profile) {
const policy::PolicyNamespace policy_namespace(policy::POLICY_DOMAIN_CHROME,
std::string());
const policy::PolicyMap& policy_map =
policy_service->GetPolicies(policy_namespace);
base::Value::Dict filtered_policies = ParseArcPoliciesToDict(policy_map);
// Add CA certificates.
AddOncCaCertsToPolicies(policy_map, &filtered_policies);
AddRequiredKeyPairs(cert_store_service, &filtered_policies);
AddChoosePrivateKeyRuleToPolicy(policy_service, cert_store_service,
&filtered_policies);
ReplaceManagedConfigurationVariables(profile, &filtered_policies);
OverrideArcPolicies(filtered_policies, policy_map, guid, is_affiliated,
profile);
return filtered_policies;
}
std::string GetFilteredJSONPolicies(policy::PolicyService* const policy_service,
const std::string& guid,
bool is_affiliated,
const CertStoreService* cert_store_service,
const Profile* profile) {
base::Value::Dict filtered_policies = GetFilteredDictPolicies(
policy_service, guid, is_affiliated, cert_store_service, profile);
std::string policy_json;
JSONStringValueSerializer serializer(&policy_json);
serializer.Serialize(filtered_policies);
return policy_json;
}
void RecordPolicyMetrics(const policy::PolicyMap& policy) {
const base::Value* const arc_enabled =
policy.GetValue(policy::key::kArcEnabled, base::Value::Type::BOOLEAN);
if (!arc_enabled || !arc_enabled->GetBool()) {
return;
}
const base::Value* const arc_policy =
policy.GetValue(policy::key::kArcPolicy, base::Value::Type::STRING);
if (arc_policy) {
policy_util::RecordPolicyMetrics(arc_policy->GetString());
}
}
// Singleton factory for ArcPolicyBridge.
class ArcPolicyBridgeFactory
: public internal::ArcBrowserContextKeyedServiceFactoryBase<
ArcPolicyBridge,
ArcPolicyBridgeFactory> {
public:
// Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
static constexpr const char* kName = "ArcPolicyBridgeFactory";
static ArcPolicyBridgeFactory* GetInstance() {
return base::Singleton<ArcPolicyBridgeFactory>::get();
}
private:
friend base::DefaultSingletonTraits<ArcPolicyBridgeFactory>;
ArcPolicyBridgeFactory() = default;
~ArcPolicyBridgeFactory() override = default;
};
} // namespace
// static
const char ArcPolicyBridge::kPackageName[] = "packageName";
// static
const char ArcPolicyBridge::kManagedConfiguration[] = "managedConfiguration";
// static
ArcPolicyBridge* ArcPolicyBridge::GetForBrowserContext(
content::BrowserContext* context) {
return ArcPolicyBridgeFactory::GetForBrowserContext(context);
}
// static
ArcPolicyBridge* ArcPolicyBridge::GetForBrowserContextForTesting(
content::BrowserContext* context) {
return ArcPolicyBridgeFactory::GetForBrowserContextForTesting(context);
}
// static
BrowserContextKeyedServiceFactory* ArcPolicyBridge::GetFactory() {
return ArcPolicyBridgeFactory::GetInstance();
}
base::WeakPtr<ArcPolicyBridge> ArcPolicyBridge::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
ArcPolicyBridge::ArcPolicyBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service)
: ArcPolicyBridge(context, bridge_service, nullptr /* policy_service */) {}
ArcPolicyBridge::ArcPolicyBridge(content::BrowserContext* context,
ArcBridgeService* bridge_service,
policy::PolicyService* policy_service)
: context_(context),
arc_bridge_service_(bridge_service),
policy_service_(policy_service),
instance_guid_(base::Uuid::GenerateRandomV4().AsLowercaseString()) {
VLOG(2) << "ArcPolicyBridge::ArcPolicyBridge";
arc_bridge_service_->policy()->SetHost(this);
arc_bridge_service_->policy()->AddObserver(this);
arc::ArcSessionManager* arc_session_manager = arc::ArcSessionManager::Get();
arc_session_manager->AddObserver(this);
}
ArcPolicyBridge::~ArcPolicyBridge() {
VLOG(2) << "ArcPolicyBridge::~ArcPolicyBridge";
if (is_policy_service_observed) {
policy_service_->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
is_policy_service_observed = false;
policy_service_ = nullptr;
}
arc::ArcSessionManager* arc_session_manager = arc::ArcSessionManager::Get();
// It can be null in unittests
if (arc_session_manager) {
arc_session_manager->RemoveObserver(this);
}
arc_bridge_service_->policy()->RemoveObserver(this);
arc_bridge_service_->policy()->SetHost(nullptr);
}
const std::string& ArcPolicyBridge::GetInstanceGuidForTesting() {
return instance_guid_;
}
void ArcPolicyBridge::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ArcPolicyBridge::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void ArcPolicyBridge::OverrideIsManagedForTesting(bool is_managed) {
is_managed_ = is_managed;
}
void ArcPolicyBridge::OnConnectionReady() {
VLOG(1) << "ArcPolicyBridge::OnConnectionReady";
InitializePolicyService();
policy_util::RecordPolicyMetrics(GetCurrentJSONPolicies());
if (!on_arc_instance_ready_callback_.is_null()) {
std::move(on_arc_instance_ready_callback_).Run();
}
}
void ArcPolicyBridge::OnConnectionClosed() {
VLOG(1) << "ArcPolicyBridge::OnConnectionClosed";
policy_service_->RemoveObserver(policy::POLICY_DOMAIN_CHROME, this);
is_policy_service_observed = false;
policy_service_ = nullptr;
}
void ArcPolicyBridge::GetPolicies(GetPoliciesCallback callback) {
VLOG(1) << "ArcPolicyBridge::GetPolicies";
std::string new_policy = GetCurrentJSONPolicies();
if (arc_policy_for_reporting_ != new_policy) {
arc_policy_for_reporting_ = new_policy;
VLOG(1) << "Policy updated. Current policy: " << new_policy;
}
for (Observer& observer : observers_) {
observer.OnPolicySent(arc_policy_for_reporting_);
}
std::move(callback).Run(arc_policy_for_reporting_);
}
void ArcPolicyBridge::ReportCompliance(const std::string& request,
ReportComplianceCallback callback) {
VLOG(1) << "ArcPolicyBridge::ReportCompliance";
if(!is_dpc_first_compliance_reported_) {
VLOG(1) << "Reporting DPC compliance for the first time";
CertStoreService* cert_store_service =
CertStoreServiceFactory::GetForBrowserContext(context_);
if(cert_store_service != nullptr) {
VLOG(1) << "Calling OnClientCertStoreChanged to update certificates";
cert_store_service->OnClientCertStoreChanged();
}
is_dpc_first_compliance_reported_ = true;
}
data_decoder::DataDecoder::ParseJsonIsolated(
request,
base::BindOnce(&ArcPolicyBridge::OnReportComplianceParse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void ArcPolicyBridge::ReportDPCVersion(const std::string& version) {
arc_dpc_version_ = version;
for (Observer& observer : observers_) {
observer.OnReportDPCVersion(version);
}
}
void ArcPolicyBridge::ReportPlayStoreLocalPolicySet(
base::Time time,
const std::vector<std::string>& package_names) {
const std::set<std::string> packages_set(package_names.begin(),
package_names.end());
for (Observer& observer : observers_) {
observer.OnPlayStoreLocalPolicySet(time, packages_set);
}
}
void ArcPolicyBridge::OnPolicyUpdated(const policy::PolicyNamespace& ns,
const policy::PolicyMap& previous,
const policy::PolicyMap& current) {
VLOG(1) << "ArcPolicyBridge::OnPolicyUpdated";
// Allow ARC activation if any app needs to be force installed when ARC on
// demand is enabled. As ARC on demand will only be enabled if there are no
// apps being installed, only current is checked here instead of the delta
// between previous and current.
ActivateArcIfRequiredByPolicy(current);
auto* instance = ARC_GET_INSTANCE_FOR_METHOD(arc_bridge_service_->policy(),
OnPolicyUpdated);
if (!instance) {
return;
}
instance->OnPolicyUpdated();
RecordPolicyMetrics(current);
}
void ArcPolicyBridge::OnArcStartDelayed() {
InitializePolicyService();
const policy::PolicyNamespace policy_namespace(
policy::POLICY_DOMAIN_CHROME,
/*component_id=*/std::string());
const policy::PolicyMap& policy_map =
policy_service_->GetPolicies(policy_namespace);
ActivateArcIfRequiredByPolicy(policy_map);
}
void ArcPolicyBridge::OnCommandReceived(
const std::string& command,
mojom::PolicyInstance::OnCommandReceivedCallback callback) {
VLOG(1) << "ArcPolicyBridge::OnCommandReceived";
auto* const instance = ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->policy(), OnCommandReceived);
if (!instance) {
VLOG(1) << "ARC not ready yet, will retry remote command once it is ready.";
DCHECK(on_arc_instance_ready_callback_.is_null());
// base::Unretained is safe here since this class owns the callback's
// lifetime.
on_arc_instance_ready_callback_ =
base::BindOnce(&ArcPolicyBridge::OnCommandReceived,
base::Unretained(this), command, std::move(callback));
return;
}
instance->OnCommandReceived(command, std::move(callback));
}
void ArcPolicyBridge::InitializePolicyService() {
if (policy_service_ == nullptr) {
auto* profile_policy_connector =
Profile::FromBrowserContext(context_)->GetProfilePolicyConnector();
policy_service_ = profile_policy_connector->policy_service();
is_managed_ = profile_policy_connector->IsManaged();
}
if (!is_policy_service_observed) {
policy_service_->AddObserver(policy::POLICY_DOMAIN_CHROME, this);
is_policy_service_observed = true;
}
}
std::string ArcPolicyBridge::GetCurrentJSONPolicies() const {
if (!is_managed_) {
return std::string();
}
const Profile* const profile = Profile::FromBrowserContext(context_);
const user_manager::User* const user =
ash::ProfileHelper::Get()->GetUserByProfile(profile);
const CertStoreService* cert_store_service =
CertStoreServiceFactory::GetForBrowserContext(context_);
return GetFilteredJSONPolicies(policy_service_, instance_guid_,
user->IsAffiliated(), cert_store_service,
profile);
}
void ArcPolicyBridge::OnReportComplianceParse(
base::OnceCallback<void(const std::string&)> callback,
data_decoder::DataDecoder::ValueOrError result) {
std::move(callback).Run(kPolicyCompliantJson);
if (!result.has_value()) {
DLOG(ERROR) << "Can't parse policy compliance report";
return;
}
Profile::FromBrowserContext(context_)->GetPrefs()->SetBoolean(
prefs::kArcPolicyComplianceReported, true);
if (result->is_dict()) {
JSONStringValueSerializer serializer(&arc_policy_compliance_report_);
serializer.Serialize(*result);
for (Observer& observer : observers_) {
observer.OnComplianceReportReceived(&*result);
}
}
}
// static
void ArcPolicyBridge::ActivateArcIfRequiredByPolicy(
const policy::PolicyMap& policy_map) {
VLOG(1) << "ArcPolicyBridge::ActivateArcIfRequiredByPolicy";
base::Value::Dict filtered_policies = ParseArcPoliciesToDict(policy_map);
base::Value::List* apps =
filtered_policies.FindList(policy_util::kArcPolicyKeyApplications);
if (apps == nullptr) {
return;
}
bool hasForceInstallApps =
std::any_of(apps->cbegin(), apps->cbegin(), [](const auto& app) {
return *app.GetDict().FindString(kPolicyAppInstallType) ==
kPolicyAppInstallTypeForceInstalled;
});
if (hasForceInstallApps) {
VLOG(1) << "Force install apps found, allowing ARC activation.";
arc::ArcSessionManager::Get()->AllowActivation(
arc::ArcSessionManager::AllowActivationReason::kForcedByPolicy);
}
}
// static
void ArcPolicyBridge::EnsureFactoryBuilt() {
ArcPolicyBridgeFactory::GetInstance();
}
} // namespace arc