root/chromeos/network/onc/onc_merger.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. MarkRecommendedFieldnames
  2. GetEditableFlags
  3. MergeDictionaries
  4. MergeNestedDictionaries
  5. MergeDictionaries
  6. HasUserPolicy
  7. HasDevicePolicy
  8. MergeListOfValues
  9. MergeValues
  10. MergeValues
  11. MergeDictionaries
  12. MergeValues
  13. MergeNestedDictionaries
  14. MergeSettingsAndPoliciesToEffective
  15. MergeSettingsAndPoliciesToAugmented

// 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_signature.h"
#include "components/onc/onc_constants.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(::onc::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() == ::onc::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 == ::onc::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 ::onc::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 = ::onc::kAugmentationUserPolicy;
    } else if (!values.device_editable) {
      result = values.device_policy;
      *which = ::onc::kAugmentationDevicePolicy;
    } else if (values.user_setting) {
      result = values.user_setting;
      *which = ::onc::kAugmentationUserSetting;
    } else if (values.shared_setting) {
      result = values.shared_setting;
      *which = ::onc::kAugmentationSharedSetting;
    } else if (values.user_policy) {
      result = values.user_policy;
      *which = ::onc::kAugmentationUserPolicy;
    } else if (values.device_policy) {
      result = values.device_policy;
      *which = ::onc::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(::onc::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(
            ::onc::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(::onc::kAugmentationUserPolicy,
                                          values.user_policy->DeepCopy());
        }
        if (values.device_policy) {
          result->SetWithoutPathExpansion(::onc::kAugmentationDevicePolicy,
                                          values.device_policy->DeepCopy());
        }
      }
      if (values.user_setting) {
        result->SetWithoutPathExpansion(::onc::kAugmentationUserSetting,
                                        values.user_setting->DeepCopy());
      }
      if (values.shared_setting) {
        result->SetWithoutPathExpansion(::onc::kAugmentationSharedSetting,
                                        values.shared_setting->DeepCopy());
      }
      if (HasUserPolicy() && values.user_editable) {
        result->SetBooleanWithoutPathExpansion(::onc::kAugmentationUserEditable,
                                               true);
      }
      if (HasDevicePolicy() && values.device_editable) {
        result->SetBooleanWithoutPathExpansion(
            ::onc::kAugmentationDeviceEditable, true);
      }
    } else {
      // This field is not part of the provided ONCSignature, thus it cannot be
      // controlled by policy.
      result->SetStringWithoutPathExpansion(
          ::onc::kAugmentationEffectiveSetting, ::onc::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

/* [<][>][^][v][top][bottom][index][help] */