onc_merger.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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// 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.
26bool 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|.
37void 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.
53DictionaryPtr 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.
74class 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.
154class 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.
260class 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
317namespace {
318
319// Returns true if all not-null values in |values| are equal to |value|.
320bool 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.
340class 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        if (!effective_value) {
385          LOG(ERROR) << "GUID field has no effective value";
386          return make_scoped_ptr<base::Value>(NULL);
387        }
388
389        // DCHECK that all provided GUIDs are identical.
390        DCHECK(AllPresentValuesEqual(values, *effective_value));
391
392        // Return the un-augmented GUID.
393        return effective_value.Pass();
394      }
395
396      if (!which_effective.empty()) {
397        augmented_value->SetStringWithoutPathExpansion(
398            ::onc::kAugmentationEffectiveSetting, which_effective);
399      }
400      bool is_credential = onc::FieldIsCredential(*signature_, key);
401
402      // Prevent credentials from being forwarded in cleartext to
403      // UI. User/shared credentials are not stored separately, so they cannot
404      // leak here.
405      if (!is_credential) {
406        if (values.user_policy) {
407          augmented_value->SetWithoutPathExpansion(
408              ::onc::kAugmentationUserPolicy, values.user_policy->DeepCopy());
409        }
410        if (values.device_policy) {
411          augmented_value->SetWithoutPathExpansion(
412              ::onc::kAugmentationDevicePolicy,
413              values.device_policy->DeepCopy());
414        }
415      }
416      if (values.user_setting) {
417        augmented_value->SetWithoutPathExpansion(
418            ::onc::kAugmentationUserSetting, values.user_setting->DeepCopy());
419      }
420      if (values.shared_setting) {
421        augmented_value->SetWithoutPathExpansion(
422            ::onc::kAugmentationSharedSetting,
423            values.shared_setting->DeepCopy());
424      }
425      if (HasUserPolicy() && values.user_editable) {
426        augmented_value->SetBooleanWithoutPathExpansion(
427            ::onc::kAugmentationUserEditable, true);
428      }
429      if (HasDevicePolicy() && values.device_editable) {
430        augmented_value->SetBooleanWithoutPathExpansion(
431            ::onc::kAugmentationDeviceEditable, true);
432      }
433    } else {
434      // This field is not part of the provided ONCSignature, thus it cannot be
435      // controlled by policy.
436      augmented_value->SetStringWithoutPathExpansion(
437          ::onc::kAugmentationEffectiveSetting, ::onc::kAugmentationUnmanaged);
438    }
439    if (augmented_value->empty())
440      augmented_value.reset();
441    return augmented_value.PassAs<base::Value>();
442  }
443
444  // MergeListOfDictionaries override.
445  virtual DictionaryPtr MergeNestedDictionaries(
446      const std::string& key,
447      const DictPtrs &dicts) OVERRIDE {
448    DictionaryPtr result;
449    if (signature_) {
450      const OncValueSignature* enclosing_signature = signature_;
451      signature_ = NULL;
452
453      const OncFieldSignature* field =
454          GetFieldSignature(*enclosing_signature, key);
455      if (field)
456        signature_ = field->value_signature;
457      result = MergeToEffective::MergeNestedDictionaries(key, dicts);
458
459      signature_ = enclosing_signature;
460    } else {
461      result = MergeToEffective::MergeNestedDictionaries(key, dicts);
462    }
463    return result.Pass();
464  }
465
466 private:
467  const OncValueSignature* signature_;
468  DISALLOW_COPY_AND_ASSIGN(MergeToAugmented);
469};
470
471}  // namespace
472
473DictionaryPtr MergeSettingsAndPoliciesToEffective(
474    const base::DictionaryValue* user_policy,
475    const base::DictionaryValue* device_policy,
476    const base::DictionaryValue* user_settings,
477    const base::DictionaryValue* shared_settings) {
478  MergeToEffective merger;
479  return merger.MergeDictionaries(
480      user_policy, device_policy, user_settings, shared_settings, NULL);
481}
482
483DictionaryPtr MergeSettingsAndPoliciesToAugmented(
484    const OncValueSignature& signature,
485    const base::DictionaryValue* user_policy,
486    const base::DictionaryValue* device_policy,
487    const base::DictionaryValue* user_settings,
488    const base::DictionaryValue* shared_settings,
489    const base::DictionaryValue* active_settings) {
490  MergeToAugmented merger;
491  return merger.MergeDictionaries(
492      signature, user_policy, device_policy, user_settings, shared_settings,
493      active_settings);
494}
495
496}  // namespace onc
497}  // namespace chromeos
498