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