| // Copyright (c) 2012 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 "chromeos/network/onc/onc_merger.h" |
| |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/logging.h" |
| #include "base/values.h" |
| #include "chromeos/network/onc/onc_constants.h" |
| #include "chromeos/network/onc/onc_signature.h" |
| |
| namespace chromeos { |
| namespace onc { |
| namespace { |
| |
| typedef scoped_ptr<base::DictionaryValue> DictionaryPtr; |
| |
| // Inserts |true| at every field name in |result| that is recommended in |
| // |policy|. |
| void MarkRecommendedFieldnames(const base::DictionaryValue& policy, |
| base::DictionaryValue* result) { |
| const base::ListValue* recommended_value = NULL; |
| if (!policy.GetListWithoutPathExpansion(kRecommended, &recommended_value)) |
| return; |
| for (base::ListValue::const_iterator it = recommended_value->begin(); |
| it != recommended_value->end(); ++it) { |
| std::string entry; |
| if ((*it)->GetAsString(&entry)) |
| result->SetBooleanWithoutPathExpansion(entry, true); |
| } |
| } |
| |
| // Returns a dictionary which contains |true| at each path that is editable by |
| // the user. No other fields are set. |
| DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) { |
| DictionaryPtr result_editable(new base::DictionaryValue); |
| MarkRecommendedFieldnames(policy, result_editable.get()); |
| |
| // Recurse into nested dictionaries. |
| for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd(); |
| it.Advance()) { |
| const base::DictionaryValue* child_policy = NULL; |
| if (it.key() == kRecommended || |
| !it.value().GetAsDictionary(&child_policy)) { |
| continue; |
| } |
| |
| result_editable->SetWithoutPathExpansion( |
| it.key(), GetEditableFlags(*child_policy).release()); |
| } |
| return result_editable.Pass(); |
| } |
| |
| // This is the base class for merging a list of DictionaryValues in |
| // parallel. See MergeDictionaries function. |
| class MergeListOfDictionaries { |
| public: |
| typedef std::vector<const base::DictionaryValue*> DictPtrs; |
| |
| MergeListOfDictionaries() { |
| } |
| |
| virtual ~MergeListOfDictionaries() { |
| } |
| |
| // For each path in any of the dictionaries |dicts|, the function |
| // MergeListOfValues is called with the list of values that are located at |
| // that path in each of the dictionaries. This function returns a new |
| // dictionary containing all results of MergeListOfValues at the respective |
| // paths. The resulting dictionary doesn't contain empty dictionaries. |
| DictionaryPtr MergeDictionaries(const DictPtrs &dicts) { |
| DictionaryPtr result(new base::DictionaryValue); |
| std::set<std::string> visited; |
| for (DictPtrs::const_iterator it_outer = dicts.begin(); |
| it_outer != dicts.end(); ++it_outer) { |
| if (!*it_outer) |
| continue; |
| |
| for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd(); |
| field.Advance()) { |
| const std::string& key = field.key(); |
| if (key == kRecommended || !visited.insert(key).second) |
| continue; |
| |
| scoped_ptr<base::Value> merged_value; |
| if (field.value().IsType(base::Value::TYPE_DICTIONARY)) { |
| DictPtrs nested_dicts; |
| for (DictPtrs::const_iterator it_inner = dicts.begin(); |
| it_inner != dicts.end(); ++it_inner) { |
| const base::DictionaryValue* nested_dict = NULL; |
| if (*it_inner) |
| (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict); |
| nested_dicts.push_back(nested_dict); |
| } |
| DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts)); |
| if (!merged_dict->empty()) |
| merged_value = merged_dict.Pass(); |
| } else { |
| std::vector<const base::Value*> values; |
| for (DictPtrs::const_iterator it_inner = dicts.begin(); |
| it_inner != dicts.end(); ++it_inner) { |
| const base::Value* value = NULL; |
| if (*it_inner) |
| (*it_inner)->GetWithoutPathExpansion(key, &value); |
| values.push_back(value); |
| } |
| merged_value = MergeListOfValues(key, values); |
| } |
| |
| if (merged_value) |
| result->SetWithoutPathExpansion(key, merged_value.release()); |
| } |
| } |
| return result.Pass(); |
| } |
| |
| protected: |
| // This function is called by MergeDictionaries for each list of values that |
| // are located at the same path in each of the dictionaries. The order of the |
| // values is the same as of the given dictionaries |dicts|. If a dictionary |
| // doesn't contain a path then it's value is NULL. |
| virtual scoped_ptr<base::Value> MergeListOfValues( |
| const std::string& key, |
| const std::vector<const base::Value*>& values) = 0; |
| |
| virtual DictionaryPtr MergeNestedDictionaries(const std::string& key, |
| const DictPtrs &dicts) { |
| return MergeDictionaries(dicts); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries); |
| }; |
| |
| // This is the base class for merging policies and user settings. |
| class MergeSettingsAndPolicies : public MergeListOfDictionaries { |
| public: |
| struct ValueParams { |
| const base::Value* user_policy; |
| const base::Value* device_policy; |
| const base::Value* user_setting; |
| const base::Value* shared_setting; |
| const base::Value* active_setting; |
| bool user_editable; |
| bool device_editable; |
| }; |
| |
| MergeSettingsAndPolicies() {} |
| |
| // Merge the provided dictionaries. For each path in any of the dictionaries, |
| // MergeValues is called. Its results are collected in a new dictionary which |
| // is then returned. The resulting dictionary never contains empty |
| // dictionaries. |
| DictionaryPtr MergeDictionaries( |
| const base::DictionaryValue* user_policy, |
| const base::DictionaryValue* device_policy, |
| const base::DictionaryValue* user_settings, |
| const base::DictionaryValue* shared_settings, |
| const base::DictionaryValue* active_settings) { |
| hasUserPolicy_ = (user_policy != NULL); |
| hasDevicePolicy_ = (device_policy != NULL); |
| |
| DictionaryPtr user_editable; |
| if (user_policy != NULL) |
| user_editable = GetEditableFlags(*user_policy); |
| |
| DictionaryPtr device_editable; |
| if (device_policy != NULL) |
| device_editable = GetEditableFlags(*device_policy); |
| |
| std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL); |
| dicts[kUserPolicyIndex] = user_policy; |
| dicts[kDevicePolicyIndex] = device_policy; |
| dicts[kUserSettingsIndex] = user_settings; |
| dicts[kSharedSettingsIndex] = shared_settings; |
| dicts[kActiveSettingsIndex] = active_settings; |
| dicts[kUserEditableIndex] = user_editable.get(); |
| dicts[kDeviceEditableIndex] = device_editable.get(); |
| return MergeListOfDictionaries::MergeDictionaries(dicts); |
| } |
| |
| protected: |
| // This function is called by MergeDictionaries for each list of values that |
| // are located at the same path in each of the dictionaries. Implementations |
| // can use the Has*Policy functions. |
| virtual scoped_ptr<base::Value> MergeValues(const std::string& key, |
| const ValueParams& values) = 0; |
| |
| // Whether a user policy was provided. |
| bool HasUserPolicy() { |
| return hasUserPolicy_; |
| } |
| |
| // Whether a device policy was provided. |
| bool HasDevicePolicy() { |
| return hasDevicePolicy_; |
| } |
| |
| // MergeListOfDictionaries override. |
| virtual scoped_ptr<base::Value> MergeListOfValues( |
| const std::string& key, |
| const std::vector<const base::Value*>& values) OVERRIDE { |
| bool user_editable = !HasUserPolicy(); |
| if (values[kUserEditableIndex]) |
| values[kUserEditableIndex]->GetAsBoolean(&user_editable); |
| |
| bool device_editable = !HasDevicePolicy(); |
| if (values[kDeviceEditableIndex]) |
| values[kDeviceEditableIndex]->GetAsBoolean(&device_editable); |
| |
| ValueParams params; |
| params.user_policy = values[kUserPolicyIndex]; |
| params.device_policy = values[kDevicePolicyIndex]; |
| params.user_setting = values[kUserSettingsIndex]; |
| params.shared_setting = values[kSharedSettingsIndex]; |
| params.active_setting = values[kActiveSettingsIndex]; |
| params.user_editable = user_editable; |
| params.device_editable = device_editable; |
| return MergeValues(key, params); |
| } |
| |
| private: |
| enum { |
| kUserPolicyIndex, |
| kDevicePolicyIndex, |
| kUserSettingsIndex, |
| kSharedSettingsIndex, |
| kActiveSettingsIndex, |
| kUserEditableIndex, |
| kDeviceEditableIndex, |
| kLastIndex |
| }; |
| |
| bool hasUserPolicy_, hasDevicePolicy_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies); |
| }; |
| |
| // Call MergeDictionaries to merge policies and settings to the effective |
| // values. This ignores the active settings of Shill. See the description of |
| // MergeSettingsAndPoliciesToEffective. |
| class MergeToEffective : public MergeSettingsAndPolicies { |
| public: |
| MergeToEffective() {} |
| |
| protected: |
| // Merges |values| to the effective value (Mandatory policy overwrites user |
| // settings overwrites shared settings overwrites recommended policy). |which| |
| // is set to the respective onc::kAugmentation* constant that indicates which |
| // source of settings is effective. Note that this function may return a NULL |
| // pointer and set |which| to kAugmentationUserPolicy, which means that the |
| // user policy didn't set a value but also didn't recommend it, thus enforcing |
| // the empty value. |
| scoped_ptr<base::Value> MergeValues(const std::string& key, |
| const ValueParams& values, |
| std::string* which) { |
| const base::Value* result = NULL; |
| which->clear(); |
| if (!values.user_editable) { |
| result = values.user_policy; |
| *which = kAugmentationUserPolicy; |
| } else if (!values.device_editable) { |
| result = values.device_policy; |
| *which = kAugmentationDevicePolicy; |
| } else if (values.user_setting) { |
| result = values.user_setting; |
| *which = kAugmentationUserSetting; |
| } else if (values.shared_setting) { |
| result = values.shared_setting; |
| *which = kAugmentationSharedSetting; |
| } else if (values.user_policy) { |
| result = values.user_policy; |
| *which = kAugmentationUserPolicy; |
| } else if (values.device_policy) { |
| result = values.device_policy; |
| *which = kAugmentationDevicePolicy; |
| } else { |
| // Can be reached if the current field is recommended, but none of the |
| // dictionaries contained a value for it. |
| } |
| if (result) |
| return make_scoped_ptr(result->DeepCopy()); |
| return scoped_ptr<base::Value>(); |
| } |
| |
| // MergeSettingsAndPolicies override. |
| virtual scoped_ptr<base::Value> MergeValues( |
| const std::string& key, |
| const ValueParams& values) OVERRIDE { |
| std::string which; |
| return MergeValues(key, values, &which); |
| } |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(MergeToEffective); |
| }; |
| |
| // Call MergeDictionaries to merge policies and settings to an augmented |
| // dictionary which contains a dictionary for each value in the original |
| // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented. |
| class MergeToAugmented : public MergeToEffective { |
| public: |
| MergeToAugmented() {} |
| |
| DictionaryPtr MergeDictionaries( |
| const OncValueSignature& signature, |
| const base::DictionaryValue* user_policy, |
| const base::DictionaryValue* device_policy, |
| const base::DictionaryValue* user_settings, |
| const base::DictionaryValue* shared_settings, |
| const base::DictionaryValue* active_settings) { |
| signature_ = &signature; |
| return MergeToEffective::MergeDictionaries(user_policy, |
| device_policy, |
| user_settings, |
| shared_settings, |
| active_settings); |
| } |
| |
| protected: |
| // MergeSettingsAndPolicies override. |
| virtual scoped_ptr<base::Value> MergeValues( |
| const std::string& key, |
| const ValueParams& values) OVERRIDE { |
| scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue); |
| if (values.active_setting) { |
| result->SetWithoutPathExpansion(kAugmentationActiveSetting, |
| values.active_setting->DeepCopy()); |
| } |
| |
| const OncFieldSignature* field = NULL; |
| if (signature_) |
| field = GetFieldSignature(*signature_, key); |
| |
| if (field) { |
| // This field is part of the provided ONCSignature, thus it can be |
| // controlled by policy. |
| std::string which_effective; |
| MergeToEffective::MergeValues(key, values, &which_effective).reset(); |
| if (!which_effective.empty()) { |
| result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting, |
| which_effective); |
| } |
| bool is_credential = onc::FieldIsCredential(*signature_, key); |
| |
| // Prevent credentials from being forwarded in cleartext to |
| // UI. User/shared credentials are not stored separately, so they cannot |
| // leak here. |
| if (!is_credential) { |
| if (values.user_policy) { |
| result->SetWithoutPathExpansion(kAugmentationUserPolicy, |
| values.user_policy->DeepCopy()); |
| } |
| if (values.device_policy) { |
| result->SetWithoutPathExpansion(kAugmentationDevicePolicy, |
| values.device_policy->DeepCopy()); |
| } |
| } |
| if (values.user_setting) { |
| result->SetWithoutPathExpansion(kAugmentationUserSetting, |
| values.user_setting->DeepCopy()); |
| } |
| if (values.shared_setting) { |
| result->SetWithoutPathExpansion(kAugmentationSharedSetting, |
| values.shared_setting->DeepCopy()); |
| } |
| if (HasUserPolicy() && values.user_editable) { |
| result->SetBooleanWithoutPathExpansion(kAugmentationUserEditable, |
| true); |
| } |
| if (HasDevicePolicy() && values.device_editable) { |
| result->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable, |
| true); |
| } |
| } else { |
| // This field is not part of the provided ONCSignature, thus it cannot be |
| // controlled by policy. |
| result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting, |
| kAugmentationUnmanaged); |
| } |
| if (result->empty()) |
| result.reset(); |
| return result.PassAs<base::Value>(); |
| } |
| |
| // MergeListOfDictionaries override. |
| virtual DictionaryPtr MergeNestedDictionaries( |
| const std::string& key, |
| const DictPtrs &dicts) OVERRIDE { |
| DictionaryPtr result; |
| if (signature_) { |
| const OncValueSignature* enclosing_signature = signature_; |
| signature_ = NULL; |
| |
| const OncFieldSignature* field = |
| GetFieldSignature(*enclosing_signature, key); |
| if (field) |
| signature_ = field->value_signature; |
| result = MergeToEffective::MergeNestedDictionaries(key, dicts); |
| |
| signature_ = enclosing_signature; |
| } else { |
| result = MergeToEffective::MergeNestedDictionaries(key, dicts); |
| } |
| return result.Pass(); |
| } |
| |
| private: |
| const OncValueSignature* signature_; |
| DISALLOW_COPY_AND_ASSIGN(MergeToAugmented); |
| }; |
| |
| } // namespace |
| |
| DictionaryPtr MergeSettingsAndPoliciesToEffective( |
| const base::DictionaryValue* user_policy, |
| const base::DictionaryValue* device_policy, |
| const base::DictionaryValue* user_settings, |
| const base::DictionaryValue* shared_settings) { |
| MergeToEffective merger; |
| return merger.MergeDictionaries( |
| user_policy, device_policy, user_settings, shared_settings, NULL); |
| } |
| |
| DictionaryPtr MergeSettingsAndPoliciesToAugmented( |
| const OncValueSignature& signature, |
| const base::DictionaryValue* user_policy, |
| const base::DictionaryValue* device_policy, |
| const base::DictionaryValue* user_settings, |
| const base::DictionaryValue* shared_settings, |
| const base::DictionaryValue* active_settings) { |
| MergeToAugmented merger; |
| return merger.MergeDictionaries( |
| signature, user_policy, device_policy, user_settings, shared_settings, |
| active_settings); |
| } |
| |
| } // namespace onc |
| } // namespace chromeos |