Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1326)

Side by Side Diff: chrome/common/extensions/permissions/permission_set.cc

Issue 10649003: Move each permission classes to its own files in extensions/permissions (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Rebase on HEAD Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "chrome/common/extensions/permissions/permission_set.h"
6
7 #include <algorithm>
8 #include <string>
9
10 #include "chrome/common/extensions/extension.h"
11 #include "chrome/common/extensions/permissions/permissions_info.h"
12 #include "chrome/common/extensions/url_pattern.h"
13 #include "chrome/common/extensions/url_pattern_set.h"
14 #include "content/public/common/url_constants.h"
15 #include "grit/generated_resources.h"
16 #include "net/base/registry_controlled_domain.h"
17 #include "ui/base/l10n/l10n_util.h"
18
19 namespace {
20
21 // Helper for GetDistinctHosts(): com > net > org > everything else.
22 bool RcdBetterThan(std::string a, std::string b) {
23 if (a == b)
24 return false;
25 if (a == "com")
26 return true;
27 if (a == "net")
28 return b != "com";
29 if (a == "org")
30 return b != "com" && b != "net";
31 return false;
32 }
33
34 // Names of API modules that can be used without listing it in the
35 // permissions section of the manifest.
36 const char* kNonPermissionModuleNames[] = {
37 "app",
38 "browserAction",
39 "devtools",
40 "events",
41 "extension",
42 "i18n",
43 "omnibox",
44 "pageAction",
45 "pageActions",
46 "permissions",
47 "runtime",
48 "test",
49 "types"
50 };
51 const size_t kNumNonPermissionModuleNames =
52 arraysize(kNonPermissionModuleNames);
53
54 // Names of functions (within modules requiring permissions) that can be used
55 // without asking for the module permission. In other words, functions you can
56 // use with no permissions specified.
57 const char* kNonPermissionFunctionNames[] = {
58 "management.getPermissionWarningsByManifest",
59 "tabs.create",
60 "tabs.onRemoved",
61 "tabs.remove",
62 "tabs.update",
63 };
64 const size_t kNumNonPermissionFunctionNames =
65 arraysize(kNonPermissionFunctionNames);
66
67 void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) {
68 DCHECK(out);
69 for (URLPatternSet::const_iterator i = set.begin(); i != set.end(); ++i) {
70 URLPattern p = *i;
71 p.SetPath("/*");
72 out->AddPattern(p);
73 }
74 }
75
76 // Strips out the API name from a function or event name.
77 // Functions will be of the form api_name.function
78 // Events will be of the form api_name/id or api_name.optional.stuff
79 std::string GetPermissionName(const std::string& function_name) {
80 size_t separator = function_name.find_first_of("./");
81 if (separator != std::string::npos)
82 return function_name.substr(0, separator);
83 else
84 return function_name;
85 }
86
87 } // namespace
88
89 namespace extensions {
90
91 //
92 // PermissionSet
93 //
94
95 PermissionSet::PermissionSet() {}
96
97 PermissionSet::PermissionSet(
98 const extensions::Extension* extension,
99 const APIPermissionSet& apis,
100 const URLPatternSet& explicit_hosts,
101 const OAuth2Scopes& scopes)
102 : apis_(apis),
103 scopes_(scopes) {
104 DCHECK(extension);
105 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_);
106 InitImplicitExtensionPermissions(extension);
107 InitEffectiveHosts();
108 }
109
110 PermissionSet::PermissionSet(
111 const APIPermissionSet& apis,
112 const URLPatternSet& explicit_hosts,
113 const URLPatternSet& scriptable_hosts)
114 : apis_(apis),
115 scriptable_hosts_(scriptable_hosts) {
116 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_);
117 InitEffectiveHosts();
118 }
119
120 PermissionSet::PermissionSet(
121 const APIPermissionSet& apis,
122 const URLPatternSet& explicit_hosts,
123 const URLPatternSet& scriptable_hosts,
124 const OAuth2Scopes& scopes)
125 : apis_(apis),
126 scriptable_hosts_(scriptable_hosts),
127 scopes_(scopes) {
128 AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_);
129 InitEffectiveHosts();
130 }
131
132 PermissionSet::PermissionSet(
133 const OAuth2Scopes& scopes)
134 : scopes_(scopes) {
135 InitEffectiveHosts();
136 }
137
138 // static
139 PermissionSet* PermissionSet::CreateDifference(
140 const PermissionSet* set1,
141 const PermissionSet* set2) {
142 scoped_refptr<PermissionSet> empty = new PermissionSet();
143 const PermissionSet* set1_safe = (set1 == NULL) ? empty : set1;
144 const PermissionSet* set2_safe = (set2 == NULL) ? empty : set2;
145
146 APIPermissionSet apis;
147 std::set_difference(set1_safe->apis().begin(), set1_safe->apis().end(),
148 set2_safe->apis().begin(), set2_safe->apis().end(),
149 std::insert_iterator<APIPermissionSet>(
150 apis, apis.begin()));
151
152 URLPatternSet explicit_hosts;
153 URLPatternSet::CreateDifference(set1_safe->explicit_hosts(),
154 set2_safe->explicit_hosts(),
155 &explicit_hosts);
156
157 URLPatternSet scriptable_hosts;
158 URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(),
159 set2_safe->scriptable_hosts(),
160 &scriptable_hosts);
161
162 OAuth2Scopes scopes;
163 std::set_difference(set1_safe->scopes().begin(), set1_safe->scopes().end(),
164 set2_safe->scopes().begin(), set2_safe->scopes().end(),
165 std::insert_iterator<OAuth2Scopes>(
166 scopes, scopes.begin()));
167
168 return new PermissionSet(
169 apis, explicit_hosts, scriptable_hosts, scopes);
170 }
171
172 // static
173 PermissionSet* PermissionSet::CreateIntersection(
174 const PermissionSet* set1,
175 const PermissionSet* set2) {
176 scoped_refptr<PermissionSet> empty = new PermissionSet();
177 const PermissionSet* set1_safe = (set1 == NULL) ? empty : set1;
178 const PermissionSet* set2_safe = (set2 == NULL) ? empty : set2;
179
180 APIPermissionSet apis;
181 std::set_intersection(set1_safe->apis().begin(), set1_safe->apis().end(),
182 set2_safe->apis().begin(), set2_safe->apis().end(),
183 std::insert_iterator<APIPermissionSet>(
184 apis, apis.begin()));
185 URLPatternSet explicit_hosts;
186 URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(),
187 set2_safe->explicit_hosts(),
188 &explicit_hosts);
189
190 URLPatternSet scriptable_hosts;
191 URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(),
192 set2_safe->scriptable_hosts(),
193 &scriptable_hosts);
194
195 OAuth2Scopes scopes;
196 std::set_intersection(set1_safe->scopes().begin(), set1_safe->scopes().end(),
197 set2_safe->scopes().begin(), set2_safe->scopes().end(),
198 std::insert_iterator<OAuth2Scopes>(
199 scopes, scopes.begin()));
200
201 return new PermissionSet(
202 apis, explicit_hosts, scriptable_hosts, scopes);
203 }
204
205 // static
206 PermissionSet* PermissionSet::CreateUnion(
207 const PermissionSet* set1,
208 const PermissionSet* set2) {
209 scoped_refptr<PermissionSet> empty = new PermissionSet();
210 const PermissionSet* set1_safe = (set1 == NULL) ? empty : set1;
211 const PermissionSet* set2_safe = (set2 == NULL) ? empty : set2;
212
213 APIPermissionSet apis;
214 std::set_union(set1_safe->apis().begin(), set1_safe->apis().end(),
215 set2_safe->apis().begin(), set2_safe->apis().end(),
216 std::insert_iterator<APIPermissionSet>(
217 apis, apis.begin()));
218
219 URLPatternSet explicit_hosts;
220 URLPatternSet::CreateUnion(set1_safe->explicit_hosts(),
221 set2_safe->explicit_hosts(),
222 &explicit_hosts);
223
224 URLPatternSet scriptable_hosts;
225 URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(),
226 set2_safe->scriptable_hosts(),
227 &scriptable_hosts);
228
229 OAuth2Scopes scopes;
230 std::set_union(set1_safe->scopes().begin(), set1_safe->scopes().end(),
231 set2_safe->scopes().begin(), set2_safe->scopes().end(),
232 std::insert_iterator<OAuth2Scopes>(
233 scopes, scopes.begin()));
234
235 return new PermissionSet(
236 apis, explicit_hosts, scriptable_hosts, scopes);
237 }
238
239 bool PermissionSet::operator==(
240 const PermissionSet& rhs) const {
241 return apis_ == rhs.apis_ &&
242 scriptable_hosts_ == rhs.scriptable_hosts_ &&
243 explicit_hosts_ == rhs.explicit_hosts_ &&
244 scopes_ == rhs.scopes_;
245 }
246
247 bool PermissionSet::Contains(const PermissionSet& set) const {
248 // Every set includes the empty set.
249 if (set.IsEmpty())
250 return true;
251
252 if (!std::includes(apis_.begin(), apis_.end(),
253 set.apis().begin(), set.apis().end()))
254 return false;
255
256 if (!explicit_hosts().Contains(set.explicit_hosts()))
257 return false;
258
259 if (!scriptable_hosts().Contains(set.scriptable_hosts()))
260 return false;
261
262 if (!std::includes(scopes_.begin(), scopes_.end(),
263 set.scopes().begin(), set.scopes().end()))
264 return false;
265
266 return true;
267 }
268
269 std::set<std::string> PermissionSet::GetAPIsAsStrings() const {
270 PermissionsInfo* info = PermissionsInfo::GetInstance();
271 std::set<std::string> apis_str;
272 for (APIPermissionSet::const_iterator i = apis_.begin();
273 i != apis_.end(); ++i) {
274 APIPermission* permission = info->GetByID(*i);
275 if (permission)
276 apis_str.insert(permission->name());
277 }
278 return apis_str;
279 }
280
281 std::set<std::string> PermissionSet::
282 GetAPIsWithAnyAccessAsStrings() const {
283 std::set<std::string> result = GetAPIsAsStrings();
284 for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i)
285 result.insert(kNonPermissionModuleNames[i]);
286 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i)
287 result.insert(GetPermissionName(kNonPermissionFunctionNames[i]));
288 return result;
289 }
290
291 bool PermissionSet::HasAnyAccessToAPI(
292 const std::string& api_name) const {
293 if (HasAccessToFunction(api_name))
294 return true;
295
296 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) {
297 if (api_name == GetPermissionName(kNonPermissionFunctionNames[i]))
298 return true;
299 }
300
301 return false;
302 }
303
304 std::set<std::string>
305 PermissionSet::GetDistinctHostsForDisplay() const {
306 return GetDistinctHosts(effective_hosts_, true, true);
307 }
308
309 PermissionMessages
310 PermissionSet::GetPermissionMessages() const {
311 PermissionMessages messages;
312
313 if (HasEffectiveFullAccess()) {
314 messages.push_back(PermissionMessage(
315 PermissionMessage::kFullAccess,
316 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)));
317 return messages;
318 }
319
320 if (HasEffectiveAccessToAllHosts()) {
321 messages.push_back(PermissionMessage(
322 PermissionMessage::kHostsAll,
323 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
324 } else {
325 std::set<std::string> hosts = GetDistinctHostsForDisplay();
326 if (!hosts.empty())
327 messages.push_back(PermissionMessage::CreateFromHostList(hosts));
328 }
329
330 std::set<PermissionMessage> simple_msgs =
331 GetSimplePermissionMessages();
332 messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end());
333
334 return messages;
335 }
336
337 std::vector<string16> PermissionSet::GetWarningMessages() const {
338 std::vector<string16> messages;
339 PermissionMessages permissions = GetPermissionMessages();
340
341 bool audio_capture = false;
342 bool video_capture = false;
343 for (PermissionMessages::const_iterator i = permissions.begin();
344 i != permissions.end(); ++i) {
345 if (i->id() == PermissionMessage::kAudioCapture)
346 audio_capture = true;
347 if (i->id() == PermissionMessage::kVideoCapture)
348 video_capture = true;
349 }
350
351 for (PermissionMessages::const_iterator i = permissions.begin();
352 i != permissions.end(); ++i) {
353 if (audio_capture && video_capture) {
354 if (i->id() == PermissionMessage::kAudioCapture) {
355 messages.push_back(l10n_util::GetStringUTF16(
356 IDS_EXTENSION_PROMPT_WARNING_AUDIO_AND_VIDEO_CAPTURE));
357 continue;
358 } else if (i->id() == PermissionMessage::kVideoCapture) {
359 // The combined message will be pushed above.
360 continue;
361 }
362 }
363
364 messages.push_back(i->message());
365 }
366
367 return messages;
368 }
369
370 bool PermissionSet::IsEmpty() const {
371 // Not default if any host permissions are present.
372 if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty()))
373 return false;
374
375 // Or if it has no api permissions.
376 return apis().empty();
377 }
378
379 bool PermissionSet::HasAPIPermission(
380 APIPermission::ID permission) const {
381 return apis().find(permission) != apis().end();
382 }
383
384 bool PermissionSet::HasAccessToFunction(
385 const std::string& function_name) const {
386 // TODO(jstritar): Embed this information in each permission and add a method
387 // like GrantsAccess(function_name) to APIPermission. A "default"
388 // permission can then handle the modules and functions that everyone can
389 // access.
390 for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) {
391 if (function_name == kNonPermissionFunctionNames[i])
392 return true;
393 }
394
395 std::string permission_name = GetPermissionName(function_name);
396 APIPermission* permission =
397 PermissionsInfo::GetInstance()->GetByName(permission_name);
398 if (permission && apis_.count(permission->id()))
399 return true;
400
401 for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) {
402 if (permission_name == kNonPermissionModuleNames[i]) {
403 return true;
404 }
405 }
406
407 return false;
408 }
409
410 bool PermissionSet::HasExplicitAccessToOrigin(
411 const GURL& origin) const {
412 return explicit_hosts().MatchesURL(origin);
413 }
414
415 bool PermissionSet::HasScriptableAccessToURL(
416 const GURL& origin) const {
417 // We only need to check our host list to verify access. The host list should
418 // already reflect any special rules (such as chrome://favicon, all hosts
419 // access, etc.).
420 return scriptable_hosts().MatchesURL(origin);
421 }
422
423 bool PermissionSet::HasEffectiveAccessToAllHosts() const {
424 // There are two ways this set can have effective access to all hosts:
425 // 1) it has an <all_urls> URL pattern.
426 // 2) it has a named permission with implied full URL access.
427 for (URLPatternSet::const_iterator host = effective_hosts().begin();
428 host != effective_hosts().end(); ++host) {
429 if (host->match_all_urls() ||
430 (host->match_subdomains() && host->host().empty()))
431 return true;
432 }
433
434 PermissionsInfo* info = PermissionsInfo::GetInstance();
435 for (APIPermissionSet::const_iterator i = apis().begin();
436 i != apis().end(); ++i) {
437 APIPermission* permission = info->GetByID(*i);
438 if (permission->implies_full_url_access())
439 return true;
440 }
441 return false;
442 }
443
444 bool PermissionSet::HasEffectiveAccessToURL(
445 const GURL& url) const {
446 return effective_hosts().MatchesURL(url);
447 }
448
449 bool PermissionSet::HasEffectiveFullAccess() const {
450 PermissionsInfo* info = PermissionsInfo::GetInstance();
451 for (APIPermissionSet::const_iterator i = apis().begin();
452 i != apis().end(); ++i) {
453 APIPermission* permission = info->GetByID(*i);
454 if (permission->implies_full_access())
455 return true;
456 }
457 return false;
458 }
459
460 bool PermissionSet::HasLessPrivilegesThan(
461 const PermissionSet* permissions) const {
462 // Things can't get worse than native code access.
463 if (HasEffectiveFullAccess())
464 return false;
465
466 // Otherwise, it's a privilege increase if the new one has full access.
467 if (permissions->HasEffectiveFullAccess())
468 return true;
469
470 if (HasLessHostPrivilegesThan(permissions))
471 return true;
472
473 if (HasLessAPIPrivilegesThan(permissions))
474 return true;
475
476 if (HasLessScopesThan(permissions))
477 return true;
478
479 return false;
480 }
481
482 PermissionSet::~PermissionSet() {}
483
484 // static
485 std::set<std::string> PermissionSet::GetDistinctHosts(
486 const URLPatternSet& host_patterns,
487 bool include_rcd,
488 bool exclude_file_scheme) {
489 // Use a vector to preserve order (also faster than a map on small sets).
490 // Each item is a host split into two parts: host without RCDs and
491 // current best RCD.
492 typedef std::vector<std::pair<std::string, std::string> > HostVector;
493 HostVector hosts_best_rcd;
494 for (URLPatternSet::const_iterator i = host_patterns.begin();
495 i != host_patterns.end(); ++i) {
496 if (exclude_file_scheme && i->scheme() == chrome::kFileScheme)
497 continue;
498
499 std::string host = i->host();
500
501 // Add the subdomain wildcard back to the host, if necessary.
502 if (i->match_subdomains())
503 host = "*." + host;
504
505 // If the host has an RCD, split it off so we can detect duplicates.
506 std::string rcd;
507 size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength(
508 host, false);
509 if (reg_len && reg_len != std::string::npos) {
510 if (include_rcd) // else leave rcd empty
511 rcd = host.substr(host.size() - reg_len);
512 host = host.substr(0, host.size() - reg_len);
513 }
514
515 // Check if we've already seen this host.
516 HostVector::iterator it = hosts_best_rcd.begin();
517 for (; it != hosts_best_rcd.end(); ++it) {
518 if (it->first == host)
519 break;
520 }
521 // If this host was found, replace the RCD if this one is better.
522 if (it != hosts_best_rcd.end()) {
523 if (include_rcd && RcdBetterThan(rcd, it->second))
524 it->second = rcd;
525 } else { // Previously unseen host, append it.
526 hosts_best_rcd.push_back(std::make_pair(host, rcd));
527 }
528 }
529
530 // Build up the final vector by concatenating hosts and RCDs.
531 std::set<std::string> distinct_hosts;
532 for (HostVector::iterator it = hosts_best_rcd.begin();
533 it != hosts_best_rcd.end(); ++it)
534 distinct_hosts.insert(it->first + it->second);
535 return distinct_hosts;
536 }
537
538 void PermissionSet::InitImplicitExtensionPermissions(
539 const extensions::Extension* extension) {
540 // Add the implied permissions.
541 if (!extension->plugins().empty())
542 apis_.insert(APIPermission::kPlugin);
543
544 if (!extension->devtools_url().is_empty())
545 apis_.insert(APIPermission::kDevtools);
546
547 // The webRequest permission implies the internal version as well.
548 if (apis_.find(APIPermission::kWebRequest) != apis_.end())
549 apis_.insert(APIPermission::kWebRequestInternal);
550
551 // The fileBrowserHandler permission implies the internal version as well.
552 if (apis_.find(APIPermission::kFileBrowserHandler) != apis_.end())
553 apis_.insert(APIPermission::kFileBrowserHandlerInternal);
554
555 // Add the scriptable hosts.
556 for (UserScriptList::const_iterator content_script =
557 extension->content_scripts().begin();
558 content_script != extension->content_scripts().end(); ++content_script) {
559 URLPatternSet::const_iterator pattern =
560 content_script->url_patterns().begin();
561 for (; pattern != content_script->url_patterns().end(); ++pattern)
562 scriptable_hosts_.AddPattern(*pattern);
563 }
564 }
565
566 void PermissionSet::InitEffectiveHosts() {
567 effective_hosts_.ClearPatterns();
568
569 URLPatternSet::CreateUnion(
570 explicit_hosts(), scriptable_hosts(), &effective_hosts_);
571 }
572
573 std::set<PermissionMessage>
574 PermissionSet::GetSimplePermissionMessages() const {
575 std::set<PermissionMessage> messages;
576 PermissionsInfo* info = PermissionsInfo::GetInstance();
577 for (APIPermissionSet::const_iterator i = apis_.begin();
578 i != apis_.end(); ++i) {
579 DCHECK_GT(PermissionMessage::kNone,
580 PermissionMessage::kUnknown);
581 APIPermission* perm = info->GetByID(*i);
582 if (perm && perm->message_id() > PermissionMessage::kNone)
583 messages.insert(perm->GetMessage());
584 }
585 return messages;
586 }
587
588 bool PermissionSet::HasLessAPIPrivilegesThan(
589 const PermissionSet* permissions) const {
590 if (permissions == NULL)
591 return false;
592
593 std::set<PermissionMessage> current_warnings =
594 GetSimplePermissionMessages();
595 std::set<PermissionMessage> new_warnings =
596 permissions->GetSimplePermissionMessages();
597 std::set<PermissionMessage> delta_warnings;
598 std::set_difference(new_warnings.begin(), new_warnings.end(),
599 current_warnings.begin(), current_warnings.end(),
600 std::inserter(delta_warnings, delta_warnings.begin()));
601
602 // We have less privileges if there are additional warnings present.
603 return !delta_warnings.empty();
604 }
605
606 bool PermissionSet::HasLessHostPrivilegesThan(
607 const PermissionSet* permissions) const {
608 // If this permission set can access any host, then it can't be elevated.
609 if (HasEffectiveAccessToAllHosts())
610 return false;
611
612 // Likewise, if the other permission set has full host access, then it must be
613 // a privilege increase.
614 if (permissions->HasEffectiveAccessToAllHosts())
615 return true;
616
617 const URLPatternSet& old_list = effective_hosts();
618 const URLPatternSet& new_list = permissions->effective_hosts();
619
620 // TODO(jstritar): This is overly conservative with respect to subdomains.
621 // For example, going from *.google.com to www.google.com will be
622 // considered an elevation, even though it is not (http://crbug.com/65337).
623 std::set<std::string> new_hosts_set(GetDistinctHosts(new_list, false, false));
624 std::set<std::string> old_hosts_set(GetDistinctHosts(old_list, false, false));
625 std::set<std::string> new_hosts_only;
626
627 std::set_difference(new_hosts_set.begin(), new_hosts_set.end(),
628 old_hosts_set.begin(), old_hosts_set.end(),
629 std::inserter(new_hosts_only, new_hosts_only.begin()));
630
631 return !new_hosts_only.empty();
632 }
633
634 bool PermissionSet::HasLessScopesThan(
635 const PermissionSet* permissions) const {
636 if (permissions == NULL)
637 return false;
638
639 OAuth2Scopes current_scopes = scopes();
640 OAuth2Scopes new_scopes = permissions->scopes();
641 OAuth2Scopes delta_scopes;
642 std::set_difference(new_scopes.begin(), new_scopes.end(),
643 current_scopes.begin(), current_scopes.end(),
644 std::inserter(delta_scopes, delta_scopes.begin()));
645
646 // We have less privileges if there are additional scopes present.
647 return !delta_scopes.empty();
648 }
649
650 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698