diff options
Diffstat (limited to 'chromium/components/policy/core/common/policy_map.cc')
-rw-r--r-- | chromium/components/policy/core/common/policy_map.cc | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/chromium/components/policy/core/common/policy_map.cc b/chromium/components/policy/core/common/policy_map.cc new file mode 100644 index 00000000000..df79bf0ff34 --- /dev/null +++ b/chromium/components/policy/core/common/policy_map.cc @@ -0,0 +1,662 @@ +// Copyright 2013 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 "components/policy/core/common/policy_map.h" + +#include <algorithm> +#include <utility> + +#include "base/callback.h" +#include "base/stl_util.h" +#include "base/strings/strcat.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "build/build_config.h" +#include "components/policy/core/common/cloud/affiliation.h" +#include "components/policy/core/common/policy_merger.h" +#include "components/policy/policy_constants.h" +#include "components/strings/grit/components_strings.h" +#include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/base/l10n/l10n_util.h" + +namespace policy { + +namespace { + +const std::u16string GetLocalizedString( + PolicyMap::Entry::L10nLookupFunction lookup, + const std::map<int, absl::optional<std::vector<std::u16string>>>& + localized_string_ids) { + std::u16string result = std::u16string(); + std::u16string line_feed = u"\n"; + for (const auto& string_pairs : localized_string_ids) { + if (string_pairs.second) + result += l10n_util::GetStringFUTF16( + string_pairs.first, string_pairs.second.value(), nullptr); + else + result += lookup.Run(string_pairs.first); + result += line_feed; + } + // Remove the trailing newline. + if (!result.empty() && result[result.length() - 1] == line_feed[0]) + result.pop_back(); + return result; +} + +// Inserts additional user affiliation IDs to the existing set. +base::flat_set<std::string> CombineIds( + const base::flat_set<std::string>& ids_first, + const base::flat_set<std::string>& ids_second) { + base::flat_set<std::string> combined_ids; + combined_ids.insert(ids_first.begin(), ids_first.end()); + combined_ids.insert(ids_second.begin(), ids_second.end()); + return combined_ids; +} + +#if !BUILDFLAG(IS_CHROMEOS) +// Returns the calculated priority of the policy entry based on the policy's +// scope and source, in addition to external factors such as precedence +// metapolicy values. Used for browser policies. +PolicyPriorityBrowser GetPriority( + PolicySource source, + PolicyScope scope, + bool cloud_policy_overrides_platform_policy, + bool cloud_user_policy_overrides_cloud_machine_policy, + bool is_user_affiliated) { + switch (source) { + case POLICY_SOURCE_ENTERPRISE_DEFAULT: + return POLICY_PRIORITY_BROWSER_ENTERPRISE_DEFAULT; + case POLICY_SOURCE_COMMAND_LINE: + return POLICY_PRIORITY_BROWSER_COMMAND_LINE; + case POLICY_SOURCE_CLOUD: + if (scope == POLICY_SCOPE_MACHINE) { + // Raise the priority of cloud machine policies only when the metapolicy + // CloudPolicyOverridesPlatformPolicy is set to true. + return cloud_policy_overrides_platform_policy + ? POLICY_PRIORITY_BROWSER_CLOUD_MACHINE_RAISED + : POLICY_PRIORITY_BROWSER_CLOUD_MACHINE; + } + if (cloud_user_policy_overrides_cloud_machine_policy && + is_user_affiliated) { + // Raise the priority of cloud user policies only when the metapolicy + // CloudUserPolicyOverridesCloudMachinePolicy is set to true and the + // user is affiliated. Its priority relative to cloud machine policies + // also depends on the value of CloudPolicyOverridesPlatformPolicy. + return cloud_policy_overrides_platform_policy + ? POLICY_PRIORITY_BROWSER_CLOUD_USER_DOUBLE_RAISED + : POLICY_PRIORITY_BROWSER_CLOUD_USER_RAISED; + } + return POLICY_PRIORITY_BROWSER_CLOUD_USER; + case POLICY_SOURCE_PRIORITY_CLOUD_DEPRECATED: + return POLICY_PRIORITY_BROWSER_CLOUD_MACHINE_RAISED; + case POLICY_SOURCE_PLATFORM: + return scope == POLICY_SCOPE_MACHINE + ? POLICY_PRIORITY_BROWSER_PLATFORM_MACHINE + : POLICY_PRIORITY_BROWSER_PLATFORM_USER; + case POLICY_SOURCE_MERGED: + return POLICY_PRIORITY_BROWSER_MERGED; + default: + NOTREACHED(); + return POLICY_PRIORITY_BROWSER_ENTERPRISE_DEFAULT; + } +} +#endif // BUILDFLAG(IS_CHROMEOS) + +} // namespace + +PolicyMap::Entry::Entry() = default; +PolicyMap::Entry::Entry( + PolicyLevel level, + PolicyScope scope, + PolicySource source, + absl::optional<base::Value> value, + std::unique_ptr<ExternalDataFetcher> external_data_fetcher) + : level(level), + scope(scope), + source(source), + external_data_fetcher(std::move(external_data_fetcher)), + value_(std::move(value)) {} + +PolicyMap::Entry::~Entry() = default; + +PolicyMap::Entry::Entry(Entry&&) noexcept = default; +PolicyMap::Entry& PolicyMap::Entry::operator=(Entry&&) noexcept = default; + +PolicyMap::Entry PolicyMap::Entry::DeepCopy() const { + Entry copy(level, scope, source, + value_ ? absl::make_optional<base::Value>(value_->Clone()) + : absl::nullopt, + external_data_fetcher + ? std::make_unique<ExternalDataFetcher>(*external_data_fetcher) + : nullptr); + copy.ignored_ = ignored_; + copy.message_ids_ = message_ids_; + copy.is_default_value_ = is_default_value_; + copy.conflicts.reserve(conflicts.size()); + for (const auto& conflict : conflicts) { + copy.AddConflictingPolicy(conflict.entry().DeepCopy()); + } + return copy; +} + +const base::Value* PolicyMap::Entry::value(base::Value::Type value_type) const { + const base::Value* value = value_unsafe(); + return value && value->type() == value_type ? value : nullptr; +} + +base::Value* PolicyMap::Entry::value(base::Value::Type value_type) { + base::Value* value = value_unsafe(); + return value && value->type() == value_type ? value : nullptr; +} + +const base::Value* PolicyMap::Entry::value_unsafe() const { + return base::OptionalOrNullptr(value_); +} + +base::Value* PolicyMap::Entry::value_unsafe() { + return base::OptionalOrNullptr(value_); +} + +void PolicyMap::Entry::set_value(absl::optional<base::Value> val) { + value_ = std::move(val); +} + +bool PolicyMap::Entry::Equals(const PolicyMap::Entry& other) const { + bool conflicts_are_equal = conflicts.size() == other.conflicts.size(); + for (size_t i = 0; conflicts_are_equal && i < conflicts.size(); ++i) + conflicts_are_equal &= + conflicts[i].entry().Equals(other.conflicts[i].entry()); + + const bool equals = + conflicts_are_equal && level == other.level && scope == other.scope && + source == other.source && // Necessary for PolicyUIHandler observers. + // They have to update when sources change. + message_ids_ == other.message_ids_ && + is_default_value_ == other.is_default_value_ && + ((!value_ && !other.value_unsafe()) || + (value_ && other.value_unsafe() && *value_ == *other.value_unsafe())) && + ExternalDataFetcher::Equals(external_data_fetcher.get(), + other.external_data_fetcher.get()); + return equals; +} + +void PolicyMap::Entry::AddMessage(MessageType type, int message_id) { + message_ids_[type].emplace(message_id, absl::nullopt); +} + +void PolicyMap::Entry::AddMessage(MessageType type, + int message_id, + std::vector<std::u16string>&& message_args) { + message_ids_[type].emplace(message_id, std::move(message_args)); +} + +void PolicyMap::Entry::ClearMessage(MessageType type, int message_id) { + if (message_ids_.find(type) == message_ids_.end() || + message_ids_[type].find(message_id) == message_ids_[type].end()) { + return; + } + message_ids_[type].erase(message_id); + if (message_ids_[type].size() == 0) + message_ids_.erase(type); +} + +void PolicyMap::Entry::AddConflictingPolicy(Entry&& conflict) { + // Move all of the newly conflicting Entry's conflicts into this Entry. + std::move(conflict.conflicts.begin(), conflict.conflicts.end(), + std::back_inserter(conflicts)); + + bool is_value_equal = (!this->value_unsafe() && !conflict.value_unsafe()) || + (this->value_unsafe() && conflict.value_unsafe() && + *this->value_unsafe() == *conflict.value_unsafe()); + + ConflictType type = + is_value_equal ? ConflictType::Supersede : ConflictType::Override; + + // Clean up conflict Entry to ensure there's no duplication since entire Entry + // is moved and treated as a freshly constructed Entry. + conflict.ClearConflicts(); + conflict.is_default_value_ = false; + conflict.message_ids_.clear(); + + // Avoid conflict nesting + conflicts.emplace_back(type, std::move(conflict)); +} + +void PolicyMap::Entry::ClearConflicts() { + conflicts.clear(); + ClearMessage(MessageType::kInfo, IDS_POLICY_CONFLICT_SAME_VALUE); + ClearMessage(MessageType::kWarning, IDS_POLICY_CONFLICT_DIFF_VALUE); +} + +bool PolicyMap::Entry::HasMessage(MessageType type) const { + return message_ids_.find(type) != message_ids_.end(); +} + +std::u16string PolicyMap::Entry::GetLocalizedMessages( + MessageType type, + L10nLookupFunction lookup) const { + if (!HasMessage(type)) { + return std::u16string(); + } + return GetLocalizedString(lookup, message_ids_.at(type)); +} + +bool PolicyMap::Entry::ignored() const { + return ignored_; +} + +void PolicyMap::Entry::SetIgnored() { + ignored_ = true; +} + +void PolicyMap::Entry::SetBlocked() { + SetIgnored(); + AddMessage(MessageType::kError, IDS_POLICY_BLOCKED); +} + +void PolicyMap::Entry::SetInvalid() { + SetIgnored(); + AddMessage(MessageType::kError, IDS_POLICY_INVALID); +} + +void PolicyMap::Entry::SetIgnoredByPolicyAtomicGroup() { + SetIgnored(); + AddMessage(MessageType::kError, IDS_POLICY_IGNORED_BY_GROUP_MERGING); +} + +bool PolicyMap::Entry::IsIgnoredByAtomicGroup() const { + return message_ids_.find(MessageType::kError) != message_ids_.end() && + message_ids_.at(MessageType::kError) + .find(IDS_POLICY_IGNORED_BY_GROUP_MERGING) != + message_ids_.at(MessageType::kError).end(); +} + +void PolicyMap::Entry::SetIsDefaultValue() { + is_default_value_ = true; +} + +bool PolicyMap::Entry::IsDefaultValue() const { + return is_default_value_; +} + +PolicyMap::EntryConflict::EntryConflict() = default; +PolicyMap::EntryConflict::EntryConflict(ConflictType type, Entry&& entry) + : conflict_type_(type), entry_(std::move(entry)) {} + +PolicyMap::EntryConflict::~EntryConflict() = default; + +PolicyMap::EntryConflict::EntryConflict(EntryConflict&&) noexcept = default; +PolicyMap::EntryConflict& PolicyMap::EntryConflict::operator=( + EntryConflict&&) noexcept = default; + +void PolicyMap::EntryConflict::SetConflictType(ConflictType type) { + conflict_type_ = type; +} + +PolicyMap::ConflictType PolicyMap::EntryConflict::conflict_type() const { + return conflict_type_; +} + +const PolicyMap::Entry& PolicyMap::EntryConflict::entry() const { + return entry_; +} + +PolicyMap::PolicyMap() = default; +PolicyMap::PolicyMap(PolicyMap&&) noexcept = default; +PolicyMap& PolicyMap::operator=(PolicyMap&&) noexcept = default; +PolicyMap::~PolicyMap() = default; + +const PolicyMap::Entry* PolicyMap::Get(const std::string& policy) const { + auto entry = map_.find(policy); + return entry != map_.end() && !entry->second.ignored() ? &entry->second + : nullptr; +} + +PolicyMap::Entry* PolicyMap::GetMutable(const std::string& policy) { + auto entry = map_.find(policy); + return entry != map_.end() && !entry->second.ignored() ? &entry->second + : nullptr; +} + +const base::Value* PolicyMap::GetValue(const std::string& policy, + base::Value::Type value_type) const { + auto* entry = Get(policy); + return entry ? entry->value(value_type) : nullptr; +} + +base::Value* PolicyMap::GetMutableValue(const std::string& policy, + base::Value::Type value_type) { + auto* entry = GetMutable(policy); + return entry ? entry->value(value_type) : nullptr; +} + +const base::Value* PolicyMap::GetValueUnsafe(const std::string& policy) const { + auto* entry = Get(policy); + return entry ? entry->value_unsafe() : nullptr; +} + +base::Value* PolicyMap::GetMutableValueUnsafe(const std::string& policy) { + auto* entry = GetMutable(policy); + return entry ? entry->value_unsafe() : nullptr; +} + +const PolicyMap::Entry* PolicyMap::GetUntrusted( + const std::string& policy) const { + auto entry = map_.find(policy); + return entry != map_.end() ? &entry->second : nullptr; +} + +PolicyMap::Entry* PolicyMap::GetMutableUntrusted(const std::string& policy) { + auto entry = map_.find(policy); + return entry != map_.end() ? &entry->second : nullptr; +} + +bool PolicyMap::IsPolicySet(const std::string& policy) const { + return GetValueUnsafe(policy) != nullptr; +} + +void PolicyMap::Set( + const std::string& policy, + PolicyLevel level, + PolicyScope scope, + PolicySource source, + absl::optional<base::Value> value, + std::unique_ptr<ExternalDataFetcher> external_data_fetcher) { + Entry entry(level, scope, source, std::move(value), + std::move(external_data_fetcher)); + Set(policy, std::move(entry)); +} + +void PolicyMap::Set(const std::string& policy, Entry entry) { + map_[policy] = std::move(entry); +} + +void PolicyMap::AddMessage(const std::string& policy, + MessageType type, + int message_id) { + map_[policy].AddMessage(type, message_id); +} + +void PolicyMap::AddMessage(const std::string& policy, + MessageType type, + int message_id, + std::vector<std::u16string>&& message_args) { + map_[policy].AddMessage(type, message_id, std::move(message_args)); +} + +bool PolicyMap::IsPolicyIgnoredByAtomicGroup(const std::string& policy) const { + const auto& entry = map_.find(policy); + return entry != map_.end() && entry->second.IsIgnoredByAtomicGroup(); +} + +void PolicyMap::SetSourceForAll(PolicySource source) { + for (auto& it : map_) { + it.second.source = source; + } +} + +void PolicyMap::SetAllInvalid() { + for (auto& it : map_) { + it.second.SetInvalid(); + } +} + +void PolicyMap::Erase(const std::string& policy) { + map_.erase(policy); +} + +PolicyMap::iterator PolicyMap::EraseIt(const_iterator it) { + return map_.erase(it); +} + +void PolicyMap::EraseMatching( + const base::RepeatingCallback<bool(const const_iterator)>& filter) { + FilterErase(filter, true); +} + +void PolicyMap::EraseNonmatching( + const base::RepeatingCallback<bool(const const_iterator)>& filter) { + FilterErase(filter, false); +} + +void PolicyMap::Swap(PolicyMap* other) { + map_.swap(other->map_); +} + +PolicyMap PolicyMap::Clone() const { + PolicyMap clone; + for (const auto& it : map_) + clone.Set(it.first, it.second.DeepCopy()); + + clone.cloud_policy_overrides_platform_policy_ = + cloud_policy_overrides_platform_policy_; + clone.cloud_user_policy_overrides_cloud_machine_policy_ = + cloud_user_policy_overrides_cloud_machine_policy_; + clone.SetUserAffiliationIds(user_affiliation_ids_); + clone.SetDeviceAffiliationIds(device_affiliation_ids_); + + return clone; +} + +void PolicyMap::MergePolicy(const std::string& policy_name, + const PolicyMap& other, + bool using_default_precedence) { + const Entry* other_policy = other.GetUntrusted(policy_name); + if (!other_policy) + return; + + Entry* policy = GetMutableUntrusted(policy_name); + Entry other_policy_copy = other_policy->DeepCopy(); + + if (!policy) { + Set(policy_name, std::move(other_policy_copy)); + return; + } + +#if BUILDFLAG(IS_CHROMEOS) + const bool other_is_higher_priority = + EntryHasHigherPriority(other_policy_copy, *policy); +#else // BUILDFLAG(IS_CHROMEOS) + const bool other_is_higher_priority = EntryHasHigherPriority( + other_policy_copy, *policy, using_default_precedence); +#endif // BUILDFLAG(IS_CHROMEOS) + + Entry& higher_policy = other_is_higher_priority ? other_policy_copy : *policy; + Entry& conflicting_policy = + other_is_higher_priority ? *policy : other_policy_copy; + + const bool overwriting_default_policy = + higher_policy.source != conflicting_policy.source && + conflicting_policy.source == POLICY_SOURCE_ENTERPRISE_DEFAULT; + if (!overwriting_default_policy) { + policy->value_unsafe() && + *other_policy_copy.value_unsafe() == *policy->value_unsafe() + ? higher_policy.AddMessage(MessageType::kInfo, + IDS_POLICY_CONFLICT_SAME_VALUE) + : higher_policy.AddMessage(MessageType::kWarning, + IDS_POLICY_CONFLICT_DIFF_VALUE); + higher_policy.AddConflictingPolicy(std::move(conflicting_policy)); + } + + if (other_is_higher_priority) + *policy = std::move(other_policy_copy); +} + +void PolicyMap::MergeFrom(const PolicyMap& other) { + DCHECK_NE(this, &other); + // Set affiliation IDs before merging policy values because user affiliation + // affects the policy precedence check. + SetUserAffiliationIds( + CombineIds(GetUserAffiliationIds(), other.GetUserAffiliationIds())); + SetDeviceAffiliationIds( + CombineIds(GetDeviceAffiliationIds(), other.GetDeviceAffiliationIds())); + + // Precedence metapolicies are merged before all other policies, including + // merging metapolicies, because their value affects policy overriding. + for (auto* policy : metapolicy::kPrecedence) { + // Default precedence is used during merging of precedence metapolicies to + // prevent circular dependencies. + MergePolicy(policy, other, true); + } + + UpdateStoredComputedMetapolicies(); + + for (const auto& policy_and_entry : other) { + // Skip precedence metapolicies since they have already been merged into the + // current PolicyMap. + if (std::find(std::begin(metapolicy::kPrecedence), + std::end(metapolicy::kPrecedence), policy_and_entry.first) != + std::end(metapolicy::kPrecedence)) { + continue; + } + + // External factors, such as the values of metapolicies, are considered. + MergePolicy(policy_and_entry.first, other, false); + } +} + +void PolicyMap::MergeValues(const std::vector<PolicyMerger*>& mergers) { + for (const auto* it : mergers) + it->Merge(this); +} + +void PolicyMap::LoadFrom(const base::DictionaryValue* policies, + PolicyLevel level, + PolicyScope scope, + PolicySource source) { + for (base::DictionaryValue::Iterator it(*policies); !it.IsAtEnd(); + it.Advance()) { + Set(it.key(), level, scope, source, it.value().Clone(), nullptr); + } +} + +bool PolicyMap::Equals(const PolicyMap& other) const { + return other.size() == size() && + std::equal(begin(), end(), other.begin(), MapEntryEquals); +} + +bool PolicyMap::empty() const { + return map_.empty(); +} + +size_t PolicyMap::size() const { + return map_.size(); +} + +PolicyMap::const_iterator PolicyMap::begin() const { + return map_.begin(); +} + +PolicyMap::const_iterator PolicyMap::end() const { + return map_.end(); +} + +PolicyMap::iterator PolicyMap::begin() { + return map_.begin(); +} + +PolicyMap::iterator PolicyMap::end() { + return map_.end(); +} + +void PolicyMap::Clear() { + map_.clear(); +} + +// static +bool PolicyMap::MapEntryEquals(const PolicyMap::PolicyMapType::value_type& a, + const PolicyMap::PolicyMapType::value_type& b) { + bool equals = a.first == b.first && a.second.Equals(b.second); + return equals; +} + +void PolicyMap::FilterErase( + const base::RepeatingCallback<bool(const const_iterator)>& filter, + bool deletion_value) { + auto iter(map_.begin()); + while (iter != map_.end()) { + if (filter.Run(iter) == deletion_value) { + map_.erase(iter++); + } else { + ++iter; + } + } +} + +bool PolicyMap::EntryHasHigherPriority(const PolicyMap::Entry& lhs, + const PolicyMap::Entry& rhs) const { + return EntryHasHigherPriority(lhs, rhs, false); +} + +bool PolicyMap::EntryHasHigherPriority(const PolicyMap::Entry& lhs, + const PolicyMap::Entry& rhs, + bool using_default_precedence) const { +#if BUILDFLAG(IS_CHROMEOS) + return std::tie(lhs.level, lhs.scope, lhs.source) > + std::tie(rhs.level, rhs.scope, rhs.source); +#else // BUILDFLAG(IS_CHROMEOS) + PolicyPriorityBrowser lhs_priority = + using_default_precedence + ? GetPriority(lhs.source, lhs.scope, false, false, false) + : GetPriority(lhs.source, lhs.scope, + cloud_policy_overrides_platform_policy_, + cloud_user_policy_overrides_cloud_machine_policy_, + is_user_affiliated_); + PolicyPriorityBrowser rhs_priority = + using_default_precedence + ? GetPriority(rhs.source, rhs.scope, false, false, false) + : GetPriority(rhs.source, rhs.scope, + cloud_policy_overrides_platform_policy_, + cloud_user_policy_overrides_cloud_machine_policy_, + is_user_affiliated_); + return std::tie(lhs.level, lhs_priority) > std::tie(rhs.level, rhs_priority); +#endif // BUILDFLAG(IS_CHROMEOS) +} + +bool PolicyMap::IsUserAffiliated() const { + return is_user_affiliated_; +} + +void PolicyMap::SetUserAffiliationIds( + const base::flat_set<std::string>& user_ids) { + user_affiliation_ids_ = {user_ids.begin(), user_ids.end()}; + UpdateStoredUserAffiliation(); +} + +const base::flat_set<std::string>& PolicyMap::GetUserAffiliationIds() const { + return user_affiliation_ids_; +} + +void PolicyMap::SetDeviceAffiliationIds( + const base::flat_set<std::string>& device_ids) { + device_affiliation_ids_ = {device_ids.begin(), device_ids.end()}; + UpdateStoredUserAffiliation(); +} + +const base::flat_set<std::string>& PolicyMap::GetDeviceAffiliationIds() const { + return device_affiliation_ids_; +} + +void PolicyMap::UpdateStoredComputedMetapolicies() { + cloud_policy_overrides_platform_policy_ = + GetValue(key::kCloudPolicyOverridesPlatformPolicy, + base::Value::Type::BOOLEAN) && + GetValue(key::kCloudPolicyOverridesPlatformPolicy, + base::Value::Type::BOOLEAN) + ->GetBool(); + + cloud_user_policy_overrides_cloud_machine_policy_ = + GetValue(key::kCloudUserPolicyOverridesCloudMachinePolicy, + base::Value::Type::BOOLEAN) && + GetValue(key::kCloudUserPolicyOverridesCloudMachinePolicy, + base::Value::Type::BOOLEAN) + ->GetBool(); +} + +void PolicyMap::UpdateStoredUserAffiliation() { + is_user_affiliated_ = + IsAffiliated(user_affiliation_ids_, device_affiliation_ids_); +} + +} // namespace policy |