onc_merger.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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
17namespace chromeos {
18namespace onc {
19namespace {
20
21typedef scoped_ptr<base::DictionaryValue> DictionaryPtr;
22
23// Inserts |true| at every field name in |result| that is recommended in
24// |policy|.
25void 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.
41DictionaryPtr 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.
62class 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.
142class 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.
248class 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.
308class 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
422DictionaryPtr 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
432DictionaryPtr 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