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