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 "extensions/common/permissions/permission_set.h"
6
7#include <algorithm>
8#include <iterator>
9#include <string>
10
11#include "extensions/common/permissions/permissions_info.h"
12#include "extensions/common/url_pattern.h"
13#include "extensions/common/url_pattern_set.h"
14#include "url/gurl.h"
15
16namespace extensions {
17
18namespace {
19
20void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) {
21  DCHECK(out);
22  for (URLPatternSet::const_iterator i = set.begin(); i != set.end(); ++i) {
23    URLPattern p = *i;
24    p.SetPath("/*");
25    out->AddPattern(p);
26  }
27}
28
29}  // namespace
30
31//
32// PermissionSet
33//
34
35PermissionSet::PermissionSet() : should_warn_all_hosts_(UNINITIALIZED) {}
36
37PermissionSet::PermissionSet(
38    const APIPermissionSet& apis,
39    const ManifestPermissionSet& manifest_permissions,
40    const URLPatternSet& explicit_hosts,
41    const URLPatternSet& scriptable_hosts)
42    : apis_(apis),
43      manifest_permissions_(manifest_permissions),
44      scriptable_hosts_(scriptable_hosts),
45      should_warn_all_hosts_(UNINITIALIZED) {
46  AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_);
47  InitImplicitPermissions();
48  InitEffectiveHosts();
49}
50
51// static
52PermissionSet* PermissionSet::CreateDifference(
53    const PermissionSet* set1,
54    const PermissionSet* set2) {
55  scoped_refptr<PermissionSet> empty = new PermissionSet();
56  const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1;
57  const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2;
58
59  APIPermissionSet apis;
60  APIPermissionSet::Difference(set1_safe->apis(), set2_safe->apis(), &apis);
61
62  ManifestPermissionSet manifest_permissions;
63  ManifestPermissionSet::Difference(set1_safe->manifest_permissions(),
64                                    set2_safe->manifest_permissions(),
65                                    &manifest_permissions);
66
67  URLPatternSet explicit_hosts;
68  URLPatternSet::CreateDifference(set1_safe->explicit_hosts(),
69                                  set2_safe->explicit_hosts(),
70                                  &explicit_hosts);
71
72  URLPatternSet scriptable_hosts;
73  URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(),
74                                  set2_safe->scriptable_hosts(),
75                                  &scriptable_hosts);
76
77  return new PermissionSet(apis, manifest_permissions,
78                           explicit_hosts, scriptable_hosts);
79}
80
81// static
82PermissionSet* PermissionSet::CreateIntersection(
83    const PermissionSet* set1,
84    const PermissionSet* set2) {
85  scoped_refptr<PermissionSet> empty = new PermissionSet();
86  const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1;
87  const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2;
88
89  APIPermissionSet apis;
90  APIPermissionSet::Intersection(set1_safe->apis(), set2_safe->apis(), &apis);
91
92  ManifestPermissionSet manifest_permissions;
93  ManifestPermissionSet::Intersection(set1_safe->manifest_permissions(),
94                                      set2_safe->manifest_permissions(),
95                                      &manifest_permissions);
96
97  URLPatternSet explicit_hosts;
98  URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(),
99                                    set2_safe->explicit_hosts(),
100                                    &explicit_hosts);
101
102  URLPatternSet scriptable_hosts;
103  URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(),
104                                    set2_safe->scriptable_hosts(),
105                                    &scriptable_hosts);
106
107  return new PermissionSet(apis, manifest_permissions,
108                           explicit_hosts, scriptable_hosts);
109}
110
111// static
112PermissionSet* PermissionSet::CreateUnion(
113    const PermissionSet* set1,
114    const PermissionSet* set2) {
115  scoped_refptr<PermissionSet> empty = new PermissionSet();
116  const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1;
117  const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2;
118
119  APIPermissionSet apis;
120  APIPermissionSet::Union(set1_safe->apis(), set2_safe->apis(), &apis);
121
122  ManifestPermissionSet manifest_permissions;
123  ManifestPermissionSet::Union(set1_safe->manifest_permissions(),
124                               set2_safe->manifest_permissions(),
125                               &manifest_permissions);
126
127  URLPatternSet explicit_hosts;
128  URLPatternSet::CreateUnion(set1_safe->explicit_hosts(),
129                             set2_safe->explicit_hosts(),
130                             &explicit_hosts);
131
132  URLPatternSet scriptable_hosts;
133  URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(),
134                             set2_safe->scriptable_hosts(),
135                             &scriptable_hosts);
136
137  return new PermissionSet(apis, manifest_permissions,
138                           explicit_hosts, scriptable_hosts);
139}
140
141bool PermissionSet::operator==(
142    const PermissionSet& rhs) const {
143  return apis_ == rhs.apis_ &&
144      manifest_permissions_ == rhs.manifest_permissions_ &&
145      scriptable_hosts_ == rhs.scriptable_hosts_ &&
146      explicit_hosts_ == rhs.explicit_hosts_;
147}
148
149bool PermissionSet::Contains(const PermissionSet& set) const {
150  return apis_.Contains(set.apis()) &&
151         manifest_permissions_.Contains(set.manifest_permissions()) &&
152         explicit_hosts().Contains(set.explicit_hosts()) &&
153         scriptable_hosts().Contains(set.scriptable_hosts());
154}
155
156std::set<std::string> PermissionSet::GetAPIsAsStrings() const {
157  std::set<std::string> apis_str;
158  for (APIPermissionSet::const_iterator i = apis_.begin();
159       i != apis_.end(); ++i) {
160    apis_str.insert(i->name());
161  }
162  return apis_str;
163}
164
165bool PermissionSet::IsEmpty() const {
166  // Not default if any host permissions are present.
167  if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty()))
168    return false;
169
170  // Or if it has no api permissions.
171  return apis().empty() && manifest_permissions().empty();
172}
173
174bool PermissionSet::HasAPIPermission(
175    APIPermission::ID id) const {
176  return apis().find(id) != apis().end();
177}
178
179bool PermissionSet::HasAPIPermission(const std::string& permission_name) const {
180  const APIPermissionInfo* permission =
181      PermissionsInfo::GetInstance()->GetByName(permission_name);
182  // Ensure our PermissionsProvider is aware of this permission.
183  CHECK(permission) << permission_name;
184  return (permission && apis_.count(permission->id()));
185}
186
187bool PermissionSet::CheckAPIPermission(APIPermission::ID permission) const {
188  return CheckAPIPermissionWithParam(permission, NULL);
189}
190
191bool PermissionSet::CheckAPIPermissionWithParam(
192    APIPermission::ID permission,
193    const APIPermission::CheckParam* param) const {
194  APIPermissionSet::const_iterator iter = apis().find(permission);
195  if (iter == apis().end())
196    return false;
197  return iter->Check(param);
198}
199
200bool PermissionSet::HasExplicitAccessToOrigin(
201    const GURL& origin) const {
202  return explicit_hosts().MatchesURL(origin);
203}
204
205bool PermissionSet::HasScriptableAccessToURL(
206    const GURL& origin) const {
207  // We only need to check our host list to verify access. The host list should
208  // already reflect any special rules (such as chrome://favicon, all hosts
209  // access, etc.).
210  return scriptable_hosts().MatchesURL(origin);
211}
212
213bool PermissionSet::HasEffectiveAccessToAllHosts() const {
214  // There are two ways this set can have effective access to all hosts:
215  //  1) it has an <all_urls> URL pattern.
216  //  2) it has a named permission with implied full URL access.
217  if (effective_hosts().MatchesAllURLs())
218    return true;
219
220  for (APIPermissionSet::const_iterator i = apis().begin();
221       i != apis().end(); ++i) {
222    if (i->info()->implies_full_url_access())
223      return true;
224  }
225  return false;
226}
227
228bool PermissionSet::ShouldWarnAllHosts() const {
229  if (should_warn_all_hosts_ == UNINITIALIZED)
230    InitShouldWarnAllHosts();
231  return should_warn_all_hosts_ == WARN_ALL_HOSTS;
232}
233
234bool PermissionSet::HasEffectiveAccessToURL(const GURL& url) const {
235  return effective_hosts().MatchesURL(url);
236}
237
238bool PermissionSet::HasEffectiveFullAccess() const {
239  for (APIPermissionSet::const_iterator i = apis().begin();
240       i != apis().end(); ++i) {
241    if (i->info()->implies_full_access())
242      return true;
243  }
244  return false;
245}
246
247PermissionSet::~PermissionSet() {}
248
249void PermissionSet::InitImplicitPermissions() {
250  // The downloads permission implies the internal version as well.
251  if (apis_.find(APIPermission::kDownloads) != apis_.end())
252    apis_.insert(APIPermission::kDownloadsInternal);
253
254  // The fileBrowserHandler permission implies the internal version as well.
255  if (apis_.find(APIPermission::kFileBrowserHandler) != apis_.end())
256    apis_.insert(APIPermission::kFileBrowserHandlerInternal);
257}
258
259void PermissionSet::InitEffectiveHosts() {
260  effective_hosts_.ClearPatterns();
261
262  URLPatternSet::CreateUnion(
263      explicit_hosts(), scriptable_hosts(), &effective_hosts_);
264}
265
266void PermissionSet::InitShouldWarnAllHosts() const {
267  if (HasEffectiveAccessToAllHosts()) {
268    should_warn_all_hosts_ = WARN_ALL_HOSTS;
269    return;
270  }
271
272  for (URLPatternSet::const_iterator iter = effective_hosts_.begin();
273       iter != effective_hosts_.end();
274       ++iter) {
275    if (iter->ImpliesAllHosts()) {
276      should_warn_all_hosts_ = WARN_ALL_HOSTS;
277      return;
278    }
279  }
280
281  should_warn_all_hosts_ = DONT_WARN_ALL_HOSTS;
282}
283
284}  // namespace extensions
285