Home | History | Annotate | Download | only in onc
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chromeos/network/onc/onc_merger.h"
      6 
      7 #include <set>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/basictypes.h"
     12 #include "base/logging.h"
     13 #include "base/values.h"
     14 #include "chromeos/network/onc/onc_constants.h"
     15 #include "chromeos/network/onc/onc_signature.h"
     16 
     17 namespace chromeos {
     18 namespace onc {
     19 namespace {
     20 
     21 typedef scoped_ptr<base::DictionaryValue> DictionaryPtr;
     22 
     23 // Inserts |true| at every field name in |result| that is recommended in
     24 // |policy|.
     25 void MarkRecommendedFieldnames(const base::DictionaryValue& policy,
     26                                base::DictionaryValue* result) {
     27   const base::ListValue* recommended_value = NULL;
     28   if (!policy.GetListWithoutPathExpansion(kRecommended, &recommended_value))
     29     return;
     30   for (base::ListValue::const_iterator it = recommended_value->begin();
     31        it != recommended_value->end(); ++it) {
     32     std::string entry;
     33     if ((*it)->GetAsString(&entry))
     34       result->SetBooleanWithoutPathExpansion(entry, true);
     35   }
     36 }
     37 
     38 // Returns a dictionary which contains |true| at each path that is editable by
     39 // the user. No other fields are set.
     40 DictionaryPtr GetEditableFlags(const base::DictionaryValue& policy) {
     41   DictionaryPtr result_editable(new base::DictionaryValue);
     42   MarkRecommendedFieldnames(policy, result_editable.get());
     43 
     44   // Recurse into nested dictionaries.
     45   for (base::DictionaryValue::Iterator it(policy); !it.IsAtEnd();
     46        it.Advance()) {
     47     const base::DictionaryValue* child_policy = NULL;
     48     if (it.key() == kRecommended ||
     49         !it.value().GetAsDictionary(&child_policy)) {
     50       continue;
     51     }
     52 
     53     result_editable->SetWithoutPathExpansion(
     54         it.key(), GetEditableFlags(*child_policy).release());
     55   }
     56   return result_editable.Pass();
     57 }
     58 
     59 // This is the base class for merging a list of DictionaryValues in
     60 // parallel. See MergeDictionaries function.
     61 class MergeListOfDictionaries {
     62  public:
     63   typedef std::vector<const base::DictionaryValue*> DictPtrs;
     64 
     65   MergeListOfDictionaries() {
     66   }
     67 
     68   virtual ~MergeListOfDictionaries() {
     69   }
     70 
     71   // For each path in any of the dictionaries |dicts|, the function
     72   // MergeListOfValues is called with the list of values that are located at
     73   // that path in each of the dictionaries. This function returns a new
     74   // dictionary containing all results of MergeListOfValues at the respective
     75   // paths. The resulting dictionary doesn't contain empty dictionaries.
     76   DictionaryPtr MergeDictionaries(const DictPtrs &dicts) {
     77     DictionaryPtr result(new base::DictionaryValue);
     78     std::set<std::string> visited;
     79     for (DictPtrs::const_iterator it_outer = dicts.begin();
     80          it_outer != dicts.end(); ++it_outer) {
     81       if (!*it_outer)
     82         continue;
     83 
     84       for (base::DictionaryValue::Iterator field(**it_outer); !field.IsAtEnd();
     85            field.Advance()) {
     86         const std::string& key = field.key();
     87         if (key == kRecommended || !visited.insert(key).second)
     88           continue;
     89 
     90         scoped_ptr<base::Value> merged_value;
     91         if (field.value().IsType(base::Value::TYPE_DICTIONARY)) {
     92           DictPtrs nested_dicts;
     93           for (DictPtrs::const_iterator it_inner = dicts.begin();
     94                it_inner != dicts.end(); ++it_inner) {
     95             const base::DictionaryValue* nested_dict = NULL;
     96             if (*it_inner)
     97               (*it_inner)->GetDictionaryWithoutPathExpansion(key, &nested_dict);
     98             nested_dicts.push_back(nested_dict);
     99           }
    100           DictionaryPtr merged_dict(MergeNestedDictionaries(key, nested_dicts));
    101           if (!merged_dict->empty())
    102             merged_value = merged_dict.Pass();
    103         } else {
    104           std::vector<const base::Value*> values;
    105           for (DictPtrs::const_iterator it_inner = dicts.begin();
    106                it_inner != dicts.end(); ++it_inner) {
    107             const base::Value* value = NULL;
    108             if (*it_inner)
    109               (*it_inner)->GetWithoutPathExpansion(key, &value);
    110             values.push_back(value);
    111           }
    112           merged_value = MergeListOfValues(key, values);
    113         }
    114 
    115         if (merged_value)
    116           result->SetWithoutPathExpansion(key, merged_value.release());
    117       }
    118     }
    119     return result.Pass();
    120   }
    121 
    122  protected:
    123   // This function is called by MergeDictionaries for each list of values that
    124   // are located at the same path in each of the dictionaries. The order of the
    125   // values is the same as of the given dictionaries |dicts|. If a dictionary
    126   // doesn't contain a path then it's value is NULL.
    127   virtual scoped_ptr<base::Value> MergeListOfValues(
    128       const std::string& key,
    129       const std::vector<const base::Value*>& values) = 0;
    130 
    131   virtual DictionaryPtr MergeNestedDictionaries(const std::string& key,
    132                                                 const DictPtrs &dicts) {
    133     return MergeDictionaries(dicts);
    134   }
    135 
    136  private:
    137   DISALLOW_COPY_AND_ASSIGN(MergeListOfDictionaries);
    138 };
    139 
    140 // This is the base class for merging policies and user settings.
    141 class MergeSettingsAndPolicies : public MergeListOfDictionaries {
    142  public:
    143   struct ValueParams {
    144     const base::Value* user_policy;
    145     const base::Value* device_policy;
    146     const base::Value* user_setting;
    147     const base::Value* shared_setting;
    148     const base::Value* active_setting;
    149     bool user_editable;
    150     bool device_editable;
    151   };
    152 
    153   MergeSettingsAndPolicies() {}
    154 
    155   // Merge the provided dictionaries. For each path in any of the dictionaries,
    156   // MergeValues is called. Its results are collected in a new dictionary which
    157   // is then returned. The resulting dictionary never contains empty
    158   // dictionaries.
    159   DictionaryPtr MergeDictionaries(
    160       const base::DictionaryValue* user_policy,
    161       const base::DictionaryValue* device_policy,
    162       const base::DictionaryValue* user_settings,
    163       const base::DictionaryValue* shared_settings,
    164       const base::DictionaryValue* active_settings) {
    165     hasUserPolicy_ = (user_policy != NULL);
    166     hasDevicePolicy_ = (device_policy != NULL);
    167 
    168     DictionaryPtr user_editable;
    169     if (user_policy != NULL)
    170       user_editable = GetEditableFlags(*user_policy);
    171 
    172     DictionaryPtr device_editable;
    173     if (device_policy != NULL)
    174       device_editable = GetEditableFlags(*device_policy);
    175 
    176     std::vector<const base::DictionaryValue*> dicts(kLastIndex, NULL);
    177     dicts[kUserPolicyIndex] = user_policy;
    178     dicts[kDevicePolicyIndex] = device_policy;
    179     dicts[kUserSettingsIndex] = user_settings;
    180     dicts[kSharedSettingsIndex] = shared_settings;
    181     dicts[kActiveSettingsIndex] = active_settings;
    182     dicts[kUserEditableIndex] = user_editable.get();
    183     dicts[kDeviceEditableIndex] = device_editable.get();
    184     return MergeListOfDictionaries::MergeDictionaries(dicts);
    185   }
    186 
    187  protected:
    188   // This function is called by MergeDictionaries for each list of values that
    189   // are located at the same path in each of the dictionaries. Implementations
    190   // can use the Has*Policy functions.
    191   virtual scoped_ptr<base::Value> MergeValues(const std::string& key,
    192                                               const ValueParams& values) = 0;
    193 
    194   // Whether a user policy was provided.
    195   bool HasUserPolicy() {
    196     return hasUserPolicy_;
    197   }
    198 
    199   // Whether a device policy was provided.
    200   bool HasDevicePolicy() {
    201     return hasDevicePolicy_;
    202   }
    203 
    204   // MergeListOfDictionaries override.
    205   virtual scoped_ptr<base::Value> MergeListOfValues(
    206       const std::string& key,
    207       const std::vector<const base::Value*>& values) OVERRIDE {
    208     bool user_editable = !HasUserPolicy();
    209     if (values[kUserEditableIndex])
    210       values[kUserEditableIndex]->GetAsBoolean(&user_editable);
    211 
    212     bool device_editable = !HasDevicePolicy();
    213     if (values[kDeviceEditableIndex])
    214       values[kDeviceEditableIndex]->GetAsBoolean(&device_editable);
    215 
    216     ValueParams params;
    217     params.user_policy = values[kUserPolicyIndex];
    218     params.device_policy = values[kDevicePolicyIndex];
    219     params.user_setting = values[kUserSettingsIndex];
    220     params.shared_setting = values[kSharedSettingsIndex];
    221     params.active_setting = values[kActiveSettingsIndex];
    222     params.user_editable = user_editable;
    223     params.device_editable = device_editable;
    224     return MergeValues(key, params);
    225   }
    226 
    227  private:
    228   enum {
    229     kUserPolicyIndex,
    230     kDevicePolicyIndex,
    231     kUserSettingsIndex,
    232     kSharedSettingsIndex,
    233     kActiveSettingsIndex,
    234     kUserEditableIndex,
    235     kDeviceEditableIndex,
    236     kLastIndex
    237   };
    238 
    239   bool hasUserPolicy_, hasDevicePolicy_;
    240 
    241   DISALLOW_COPY_AND_ASSIGN(MergeSettingsAndPolicies);
    242 };
    243 
    244 // Call MergeDictionaries to merge policies and settings to the effective
    245 // values. This ignores the active settings of Shill. See the description of
    246 // MergeSettingsAndPoliciesToEffective.
    247 class MergeToEffective : public MergeSettingsAndPolicies {
    248  public:
    249   MergeToEffective() {}
    250 
    251  protected:
    252   // Merges |values| to the effective value (Mandatory policy overwrites user
    253   // settings overwrites shared settings overwrites recommended policy). |which|
    254   // is set to the respective onc::kAugmentation* constant that indicates which
    255   // source of settings is effective. Note that this function may return a NULL
    256   // pointer and set |which| to kAugmentationUserPolicy, which means that the
    257   // user policy didn't set a value but also didn't recommend it, thus enforcing
    258   // the empty value.
    259   scoped_ptr<base::Value> MergeValues(const std::string& key,
    260                                       const ValueParams& values,
    261                                       std::string* which) {
    262     const base::Value* result = NULL;
    263     which->clear();
    264     if (!values.user_editable) {
    265       result = values.user_policy;
    266       *which = kAugmentationUserPolicy;
    267     } else if (!values.device_editable) {
    268       result = values.device_policy;
    269       *which = kAugmentationDevicePolicy;
    270     } else if (values.user_setting) {
    271       result = values.user_setting;
    272       *which = kAugmentationUserSetting;
    273     } else if (values.shared_setting) {
    274       result = values.shared_setting;
    275       *which = kAugmentationSharedSetting;
    276     } else if (values.user_policy) {
    277       result = values.user_policy;
    278       *which = kAugmentationUserPolicy;
    279     } else if (values.device_policy) {
    280       result = values.device_policy;
    281       *which = kAugmentationDevicePolicy;
    282     } else {
    283       // Can be reached if the current field is recommended, but none of the
    284       // dictionaries contained a value for it.
    285     }
    286     if (result)
    287       return make_scoped_ptr(result->DeepCopy());
    288     return scoped_ptr<base::Value>();
    289   }
    290 
    291   // MergeSettingsAndPolicies override.
    292   virtual scoped_ptr<base::Value> MergeValues(
    293       const std::string& key,
    294       const ValueParams& values) OVERRIDE {
    295     std::string which;
    296     return MergeValues(key, values, &which);
    297   }
    298 
    299  private:
    300   DISALLOW_COPY_AND_ASSIGN(MergeToEffective);
    301 };
    302 
    303 // Call MergeDictionaries to merge policies and settings to an augmented
    304 // dictionary which contains a dictionary for each value in the original
    305 // dictionaries. See the description of MergeSettingsAndPoliciesToAugmented.
    306 class MergeToAugmented : public MergeToEffective {
    307  public:
    308   MergeToAugmented() {}
    309 
    310   DictionaryPtr MergeDictionaries(
    311       const OncValueSignature& signature,
    312       const base::DictionaryValue* user_policy,
    313       const base::DictionaryValue* device_policy,
    314       const base::DictionaryValue* user_settings,
    315       const base::DictionaryValue* shared_settings,
    316       const base::DictionaryValue* active_settings) {
    317     signature_ = &signature;
    318     return MergeToEffective::MergeDictionaries(user_policy,
    319                                                device_policy,
    320                                                user_settings,
    321                                                shared_settings,
    322                                                active_settings);
    323   }
    324 
    325  protected:
    326   // MergeSettingsAndPolicies override.
    327   virtual scoped_ptr<base::Value> MergeValues(
    328       const std::string& key,
    329       const ValueParams& values) OVERRIDE {
    330     scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
    331     if (values.active_setting) {
    332       result->SetWithoutPathExpansion(kAugmentationActiveSetting,
    333                                       values.active_setting->DeepCopy());
    334     }
    335 
    336     const OncFieldSignature* field = NULL;
    337     if (signature_)
    338       field = GetFieldSignature(*signature_, key);
    339 
    340     if (field) {
    341       // This field is part of the provided ONCSignature, thus it can be
    342       // controlled by policy.
    343       std::string which_effective;
    344       MergeToEffective::MergeValues(key, values, &which_effective).reset();
    345       if (!which_effective.empty()) {
    346         result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting,
    347                                               which_effective);
    348       }
    349       bool is_credential = onc::FieldIsCredential(*signature_, key);
    350 
    351       // Prevent credentials from being forwarded in cleartext to
    352       // UI. User/shared credentials are not stored separately, so they cannot
    353       // leak here.
    354       if (!is_credential) {
    355         if (values.user_policy) {
    356           result->SetWithoutPathExpansion(kAugmentationUserPolicy,
    357                                           values.user_policy->DeepCopy());
    358         }
    359         if (values.device_policy) {
    360           result->SetWithoutPathExpansion(kAugmentationDevicePolicy,
    361                                           values.device_policy->DeepCopy());
    362         }
    363       }
    364       if (values.user_setting) {
    365         result->SetWithoutPathExpansion(kAugmentationUserSetting,
    366                                         values.user_setting->DeepCopy());
    367       }
    368       if (values.shared_setting) {
    369         result->SetWithoutPathExpansion(kAugmentationSharedSetting,
    370                                         values.shared_setting->DeepCopy());
    371       }
    372       if (HasUserPolicy() && values.user_editable) {
    373         result->SetBooleanWithoutPathExpansion(kAugmentationUserEditable,
    374                                                true);
    375       }
    376       if (HasDevicePolicy() && values.device_editable) {
    377         result->SetBooleanWithoutPathExpansion(kAugmentationDeviceEditable,
    378                                                true);
    379       }
    380     } else {
    381       // This field is not part of the provided ONCSignature, thus it cannot be
    382       // controlled by policy.
    383       result->SetStringWithoutPathExpansion(kAugmentationEffectiveSetting,
    384                                             kAugmentationUnmanaged);
    385     }
    386     if (result->empty())
    387       result.reset();
    388     return result.PassAs<base::Value>();
    389   }
    390 
    391   // MergeListOfDictionaries override.
    392   virtual DictionaryPtr MergeNestedDictionaries(
    393       const std::string& key,
    394       const DictPtrs &dicts) OVERRIDE {
    395     DictionaryPtr result;
    396     if (signature_) {
    397       const OncValueSignature* enclosing_signature = signature_;
    398       signature_ = NULL;
    399 
    400       const OncFieldSignature* field =
    401           GetFieldSignature(*enclosing_signature, key);
    402       if (field)
    403         signature_ = field->value_signature;
    404       result = MergeToEffective::MergeNestedDictionaries(key, dicts);
    405 
    406       signature_ = enclosing_signature;
    407     } else {
    408       result = MergeToEffective::MergeNestedDictionaries(key, dicts);
    409     }
    410     return result.Pass();
    411   }
    412 
    413  private:
    414   const OncValueSignature* signature_;
    415   DISALLOW_COPY_AND_ASSIGN(MergeToAugmented);
    416 };
    417 
    418 }  // namespace
    419 
    420 DictionaryPtr MergeSettingsAndPoliciesToEffective(
    421     const base::DictionaryValue* user_policy,
    422     const base::DictionaryValue* device_policy,
    423     const base::DictionaryValue* user_settings,
    424     const base::DictionaryValue* shared_settings) {
    425   MergeToEffective merger;
    426   return merger.MergeDictionaries(
    427       user_policy, device_policy, user_settings, shared_settings, NULL);
    428 }
    429 
    430 DictionaryPtr MergeSettingsAndPoliciesToAugmented(
    431     const OncValueSignature& signature,
    432     const base::DictionaryValue* user_policy,
    433     const base::DictionaryValue* device_policy,
    434     const base::DictionaryValue* user_settings,
    435     const base::DictionaryValue* shared_settings,
    436     const base::DictionaryValue* active_settings) {
    437   MergeToAugmented merger;
    438   return merger.MergeDictionaries(
    439       signature, user_policy, device_policy, user_settings, shared_settings,
    440       active_settings);
    441 }
    442 
    443 }  // namespace onc
    444 }  // namespace chromeos
    445