1// Copyright 2013 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 "chrome/browser/extensions/policy_handlers.h"
6
7#include "base/logging.h"
8#include "base/prefs/pref_value_map.h"
9#include "chrome/browser/extensions/external_policy_loader.h"
10#include "chrome/common/pref_names.h"
11#include "components/crx_file/id_util.h"
12#include "components/policy/core/browser/policy_error_map.h"
13#include "components/policy/core/common/policy_map.h"
14#include "extensions/browser/pref_names.h"
15#include "extensions/common/extension.h"
16#include "grit/components_strings.h"
17#include "policy/policy_constants.h"
18
19namespace extensions {
20
21// ExtensionListPolicyHandler implementation -----------------------------------
22
23ExtensionListPolicyHandler::ExtensionListPolicyHandler(const char* policy_name,
24                                                       const char* pref_path,
25                                                       bool allow_wildcards)
26    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST),
27      pref_path_(pref_path),
28      allow_wildcards_(allow_wildcards) {}
29
30ExtensionListPolicyHandler::~ExtensionListPolicyHandler() {}
31
32bool ExtensionListPolicyHandler::CheckPolicySettings(
33    const policy::PolicyMap& policies,
34    policy::PolicyErrorMap* errors) {
35  return CheckAndGetList(policies, errors, NULL);
36}
37
38void ExtensionListPolicyHandler::ApplyPolicySettings(
39    const policy::PolicyMap& policies,
40    PrefValueMap* prefs) {
41  scoped_ptr<base::ListValue> list;
42  policy::PolicyErrorMap errors;
43  if (CheckAndGetList(policies, &errors, &list) && list)
44    prefs->SetValue(pref_path(), list.release());
45}
46
47const char* ExtensionListPolicyHandler::pref_path() const {
48  return pref_path_;
49}
50
51bool ExtensionListPolicyHandler::CheckAndGetList(
52    const policy::PolicyMap& policies,
53    policy::PolicyErrorMap* errors,
54    scoped_ptr<base::ListValue>* extension_ids) {
55  if (extension_ids)
56    extension_ids->reset();
57
58  const base::Value* value = NULL;
59  if (!CheckAndGetValue(policies, errors, &value))
60    return false;
61
62  if (!value)
63    return true;
64
65  const base::ListValue* list_value = NULL;
66  if (!value->GetAsList(&list_value)) {
67    NOTREACHED();
68    return false;
69  }
70
71  // Filter the list, rejecting any invalid extension IDs.
72  scoped_ptr<base::ListValue> filtered_list(new base::ListValue());
73  for (base::ListValue::const_iterator entry(list_value->begin());
74       entry != list_value->end(); ++entry) {
75    std::string id;
76    if (!(*entry)->GetAsString(&id)) {
77      errors->AddError(policy_name(),
78                       entry - list_value->begin(),
79                       IDS_POLICY_TYPE_ERROR,
80                       ValueTypeToString(base::Value::TYPE_STRING));
81      continue;
82    }
83    if (!(allow_wildcards_ && id == "*") && !crx_file::id_util::IdIsValid(id)) {
84      errors->AddError(policy_name(),
85                       entry - list_value->begin(),
86                       IDS_POLICY_VALUE_FORMAT_ERROR);
87      continue;
88    }
89    filtered_list->Append(new base::StringValue(id));
90  }
91
92  if (extension_ids)
93    *extension_ids = filtered_list.Pass();
94
95  return true;
96}
97
98// ExtensionInstallForcelistPolicyHandler implementation -----------------------
99
100ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler()
101    : policy::TypeCheckingPolicyHandler(policy::key::kExtensionInstallForcelist,
102                                        base::Value::TYPE_LIST) {}
103
104ExtensionInstallForcelistPolicyHandler::
105    ~ExtensionInstallForcelistPolicyHandler() {}
106
107bool ExtensionInstallForcelistPolicyHandler::CheckPolicySettings(
108    const policy::PolicyMap& policies,
109    policy::PolicyErrorMap* errors) {
110  const base::Value* value;
111  return CheckAndGetValue(policies, errors, &value) &&
112      ParseList(value, NULL, errors);
113}
114
115void ExtensionInstallForcelistPolicyHandler::ApplyPolicySettings(
116    const policy::PolicyMap& policies,
117    PrefValueMap* prefs) {
118  const base::Value* value = NULL;
119  scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
120  if (CheckAndGetValue(policies, NULL, &value) &&
121      value &&
122      ParseList(value, dict.get(), NULL)) {
123    prefs->SetValue(pref_names::kInstallForceList, dict.release());
124  }
125}
126
127bool ExtensionInstallForcelistPolicyHandler::ParseList(
128    const base::Value* policy_value,
129    base::DictionaryValue* extension_dict,
130    policy::PolicyErrorMap* errors) {
131  if (!policy_value)
132    return true;
133
134  const base::ListValue* policy_list_value = NULL;
135  if (!policy_value->GetAsList(&policy_list_value)) {
136    // This should have been caught in CheckPolicySettings.
137    NOTREACHED();
138    return false;
139  }
140
141  for (base::ListValue::const_iterator entry(policy_list_value->begin());
142       entry != policy_list_value->end(); ++entry) {
143    std::string entry_string;
144    if (!(*entry)->GetAsString(&entry_string)) {
145      if (errors) {
146        errors->AddError(policy_name(),
147                         entry - policy_list_value->begin(),
148                         IDS_POLICY_TYPE_ERROR,
149                         ValueTypeToString(base::Value::TYPE_STRING));
150      }
151      continue;
152    }
153
154    // Each string item of the list has the following form:
155    // <extension_id>;<update_url>
156    // Note: The update URL might also contain semicolons.
157    size_t pos = entry_string.find(';');
158    if (pos == std::string::npos) {
159      if (errors) {
160        errors->AddError(policy_name(),
161                         entry - policy_list_value->begin(),
162                         IDS_POLICY_VALUE_FORMAT_ERROR);
163      }
164      continue;
165    }
166
167    std::string extension_id = entry_string.substr(0, pos);
168    std::string update_url = entry_string.substr(pos+1);
169    if (!crx_file::id_util::IdIsValid(extension_id) ||
170        !GURL(update_url).is_valid()) {
171      if (errors) {
172        errors->AddError(policy_name(),
173                         entry - policy_list_value->begin(),
174                         IDS_POLICY_VALUE_FORMAT_ERROR);
175      }
176      continue;
177    }
178
179    if (extension_dict) {
180      extensions::ExternalPolicyLoader::AddExtension(
181          extension_dict, extension_id, update_url);
182    }
183  }
184
185  return true;
186}
187
188// ExtensionURLPatternListPolicyHandler implementation -------------------------
189
190ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler(
191    const char* policy_name,
192    const char* pref_path)
193    : policy::TypeCheckingPolicyHandler(policy_name, base::Value::TYPE_LIST),
194      pref_path_(pref_path) {}
195
196ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {}
197
198bool ExtensionURLPatternListPolicyHandler::CheckPolicySettings(
199    const policy::PolicyMap& policies,
200    policy::PolicyErrorMap* errors) {
201  const base::Value* value = NULL;
202  if (!CheckAndGetValue(policies, errors, &value))
203    return false;
204
205  if (!value)
206    return true;
207
208  const base::ListValue* list_value = NULL;
209  if (!value->GetAsList(&list_value)) {
210    NOTREACHED();
211    return false;
212  }
213
214  // Check that the list contains valid URLPattern strings only.
215  for (base::ListValue::const_iterator entry(list_value->begin());
216       entry != list_value->end(); ++entry) {
217    std::string url_pattern_string;
218    if (!(*entry)->GetAsString(&url_pattern_string)) {
219      errors->AddError(policy_name(),
220                       entry - list_value->begin(),
221                       IDS_POLICY_TYPE_ERROR,
222                       ValueTypeToString(base::Value::TYPE_STRING));
223      return false;
224    }
225
226    URLPattern pattern(URLPattern::SCHEME_ALL);
227    if (pattern.Parse(url_pattern_string) != URLPattern::PARSE_SUCCESS) {
228      errors->AddError(policy_name(),
229                       entry - list_value->begin(),
230                       IDS_POLICY_VALUE_FORMAT_ERROR);
231      return false;
232    }
233  }
234
235  return true;
236}
237
238void ExtensionURLPatternListPolicyHandler::ApplyPolicySettings(
239    const policy::PolicyMap& policies,
240    PrefValueMap* prefs) {
241  if (!pref_path_)
242    return;
243  const base::Value* value = policies.GetValue(policy_name());
244  if (value)
245    prefs->SetValue(pref_path_, value->DeepCopy());
246}
247
248}  // namespace extensions
249