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

Side by Side Diff: extensions/common/manifest_handlers/permissions_parser.cc

Issue 309533007: Refactor PermissionsData pt1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 6 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2014 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/manifest_handlers/permissions_parser.h"
6
7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "content/public/common/url_constants.h"
12 #include "extensions/common/error_utils.h"
13 #include "extensions/common/extension.h"
14 #include "extensions/common/extensions_client.h"
15 #include "extensions/common/features/feature.h"
16 #include "extensions/common/features/feature_provider.h"
17 #include "extensions/common/manifest.h"
18 #include "extensions/common/manifest_constants.h"
19 #include "extensions/common/manifest_handler.h"
20 #include "extensions/common/permissions/api_permission_set.h"
21 #include "extensions/common/permissions/permission_set.h"
22 #include "extensions/common/permissions/permissions_data.h"
23 #include "extensions/common/switches.h"
24 #include "extensions/common/url_pattern_set.h"
25 #include "url/url_constants.h"
26
27 namespace extensions {
28
29 namespace {
30
31 namespace keys = manifest_keys;
32 namespace errors = manifest_errors;
33
34 struct ManifestPermissions : public Extension::ManifestData {
35 ManifestPermissions(scoped_refptr<const PermissionSet> permissions);
36 virtual ~ManifestPermissions();
37
38 scoped_refptr<const PermissionSet> permissions;
39 };
40
41 ManifestPermissions::ManifestPermissions(
42 scoped_refptr<const PermissionSet> permissions) : permissions(permissions) {
43 }
44
45 ManifestPermissions::~ManifestPermissions() {
46 }
47
48 // Custom checks for the experimental permission that can't be expressed in
49 // _permission_features.json.
50 bool CanSpecifyExperimentalPermission(const Extension* extension) {
51 if (extension->location() == Manifest::COMPONENT)
52 return true;
53
54 if (CommandLine::ForCurrentProcess()->HasSwitch(
55 switches::kEnableExperimentalExtensionApis)) {
56 return true;
57 }
58
59 // We rely on the webstore to check access to experimental. This way we can
60 // whitelist extensions to have access to experimental in just the store, and
61 // not have to push a new version of the client.
62 if (extension->from_webstore())
63 return true;
64
65 return false;
66 }
67
68 // Checks whether the host |pattern| is allowed for the given |extension|,
69 // given API permissions |permissions|.
70 bool CanSpecifyHostPermission(const Extension* extension,
71 const URLPattern& pattern,
72 const APIPermissionSet& permissions) {
73 if (!pattern.match_all_urls() &&
74 pattern.MatchesScheme(content::kChromeUIScheme)) {
75 URLPatternSet chrome_scheme_hosts = ExtensionsClient::Get()->
76 GetPermittedChromeSchemeHosts(extension, permissions);
77 if (chrome_scheme_hosts.ContainsPattern(pattern))
78 return true;
79
80 // Component extensions can have access to all of chrome://*.
81 if (PermissionsData::CanExecuteScriptEverywhere(extension))
82 return true;
83
84 if (CommandLine::ForCurrentProcess()->HasSwitch(
85 switches::kExtensionsOnChromeURLs)) {
86 return true;
87 }
88
89 // TODO(aboxhall): return from_webstore() when webstore handles blocking
90 // extensions which request chrome:// urls
91 return false;
92 }
93
94 // Otherwise, the valid schemes were handled by URLPattern.
95 return true;
96 }
97
98 // Parses the host and api permissions from the specified permission |key|
99 // from |extension|'s manifest.
100 bool ParseHelper(Extension* extension,
101 const char* key,
102 APIPermissionSet* api_permissions,
103 URLPatternSet* host_permissions,
104 base::string16* error) {
105 if (!extension->manifest()->HasKey(key))
106 return true;
107
108 const base::ListValue* permissions = NULL;
109 if (!extension->manifest()->GetList(key, &permissions)) {
110 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
111 std::string());
112 return false;
113 }
114
115 // NOTE: We need to get the APIPermission before we check if features
116 // associated with them are available because the feature system does not
117 // know about aliases.
118
119 std::vector<std::string> host_data;
120 if (!APIPermissionSet::ParseFromJSON(
121 permissions, APIPermissionSet::kDisallowInternalPermissions,
122 api_permissions, error, &host_data)) {
123 return false;
124 }
125
126 // Verify feature availability of permissions.
127 std::vector<APIPermission::ID> to_remove;
128 const FeatureProvider* permission_features =
129 FeatureProvider::GetPermissionFeatures();
130 for (APIPermissionSet::const_iterator iter = api_permissions->begin();
131 iter != api_permissions->end(); ++iter) {
132 Feature* feature = permission_features->GetFeature(iter->name());
133
134 // The feature should exist since we just got an APIPermission for it. The
135 // two systems should be updated together whenever a permission is added.
136 DCHECK(feature) << "Could not find feature for " << iter->name();
137 // http://crbug.com/176381
138 if (!feature) {
139 to_remove.push_back(iter->id());
140 continue;
141 }
142
143 Feature::Availability availability =
144 feature->IsAvailableToExtension(extension);
145 if (!availability.is_available()) {
146 // Don't fail, but warn the developer that the manifest contains
147 // unrecognized permissions. This may happen legitimately if the
148 // extensions requests platform- or channel-specific permissions.
149 extension->AddInstallWarning(InstallWarning(availability.message(),
150 feature->name()));
151 to_remove.push_back(iter->id());
152 continue;
153 }
154
155 if (iter->id() == APIPermission::kExperimental) {
156 if (!CanSpecifyExperimentalPermission(extension)) {
157 *error = base::ASCIIToUTF16(errors::kExperimentalFlagRequired);
158 return false;
159 }
160 }
161 }
162
163 api_permissions->AddImpliedPermissions();
164
165 // Remove permissions that are not available to this extension.
166 for (std::vector<APIPermission::ID>::const_iterator iter = to_remove.begin();
167 iter != to_remove.end(); ++iter) {
168 api_permissions->erase(*iter);
169 }
170
171 // Parse host pattern permissions.
172 const int kAllowedSchemes =
173 PermissionsData::CanExecuteScriptEverywhere(extension) ?
174 URLPattern::SCHEME_ALL : Extension::kValidHostPermissionSchemes;
175
176 for (std::vector<std::string>::const_iterator iter = host_data.begin();
177 iter != host_data.end(); ++iter) {
178 const std::string& permission_str = *iter;
179
180 // Check if it's a host pattern permission.
181 URLPattern pattern = URLPattern(kAllowedSchemes);
182 URLPattern::ParseResult parse_result = pattern.Parse(permission_str);
183 if (parse_result == URLPattern::PARSE_SUCCESS) {
184 // The path component is not used for host permissions, so we force it
185 // to match all paths.
186 pattern.SetPath("/*");
187 int valid_schemes = pattern.valid_schemes();
188 if (pattern.MatchesScheme(url::kFileScheme) &&
189 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
190 extension->set_wants_file_access(true);
191 if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS))
192 valid_schemes &= ~URLPattern::SCHEME_FILE;
193 }
194
195 if (pattern.scheme() != content::kChromeUIScheme &&
196 !PermissionsData::CanExecuteScriptEverywhere(extension)) {
197 // Keep chrome:// in allowed schemes only if it's explicitly requested
198 // or CanExecuteScriptEverywhere is true. If the
199 // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
200 // will fail, so don't check the flag here.
201 valid_schemes &= ~URLPattern::SCHEME_CHROMEUI;
202 }
203 pattern.SetValidSchemes(valid_schemes);
204
205 if (!CanSpecifyHostPermission(extension, pattern, *api_permissions)) {
206 // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
207 // below).
208 extension->AddInstallWarning(InstallWarning(
209 ErrorUtils::FormatErrorMessage(
210 errors::kInvalidPermissionScheme, permission_str),
211 key,
212 permission_str));
213 continue;
214 }
215
216 host_permissions->AddPattern(pattern);
217 // We need to make sure all_urls matches chrome://favicon and (maybe)
218 // chrome://thumbnail, so add them back in to host_permissions separately.
219 if (pattern.match_all_urls()) {
220 host_permissions->AddPatterns(
221 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
222 extension, *api_permissions));
223 }
224 continue;
225 }
226
227 // It's probably an unknown API permission. Do not throw an error so
228 // extensions can retain backwards compatability (http://crbug.com/42742).
229 extension->AddInstallWarning(InstallWarning(
230 ErrorUtils::FormatErrorMessage(
231 manifest_errors::kPermissionUnknownOrMalformed,
232 permission_str),
233 key,
234 permission_str));
235 }
236
237 return true;
238 }
239
240 } // namespace
241
242 struct PermissionsParser::InitialPermissions {
243 APIPermissionSet api_permissions;
244 ManifestPermissionSet manifest_permissions;
245 URLPatternSet host_permissions;
246 URLPatternSet scriptable_hosts;
247 };
248
249 PermissionsParser::PermissionsParser() {
250 }
251
252 PermissionsParser::~PermissionsParser() {
253 }
254
255 bool PermissionsParser::Parse(Extension* extension, base::string16* error) {
256 initial_required_permissions_.reset(new InitialPermissions);
257 if (!ParseHelper(extension,
258 keys::kPermissions,
259 &initial_required_permissions_->api_permissions,
260 &initial_required_permissions_->host_permissions,
261 error)) {
262 return false;
263 }
264
265 initial_optional_permissions_.reset(new InitialPermissions);
266 if (!ParseHelper(extension,
267 keys::kOptionalPermissions,
268 &initial_optional_permissions_->api_permissions,
269 &initial_optional_permissions_->host_permissions,
270 error)) {
271 return false;
272 }
273
274 return true;
275 }
276
277 void PermissionsParser::Finalize(Extension* extension) {
278 ManifestHandler::AddExtensionInitialRequiredPermissions(
279 extension, &initial_required_permissions_->manifest_permissions);
280
281 scoped_refptr<const PermissionSet> required_permissions(new PermissionSet(
282 initial_required_permissions_->api_permissions,
283 initial_required_permissions_->manifest_permissions,
284 initial_required_permissions_->host_permissions,
285 initial_required_permissions_->scriptable_hosts));
286 extension->SetManifestData(keys::kPermissions,
287 new ManifestPermissions(required_permissions));
288
289 scoped_refptr<const PermissionSet> optional_permissions(new PermissionSet(
290 initial_optional_permissions_->api_permissions,
291 initial_optional_permissions_->manifest_permissions,
292 initial_optional_permissions_->host_permissions,
293 URLPatternSet()));
294 extension->SetManifestData(keys::kOptionalPermissions,
295 new ManifestPermissions(optional_permissions));
296 }
297
298 // static
299 void PermissionsParser::AddAPIPermission(Extension* extension,
300 APIPermission::ID permission) {
301 DCHECK(extension->permissions_parser());
302 extension->permissions_parser()->initial_required_permissions_
303 ->api_permissions.insert(permission);
304
305 }
306
307 // static
308 void PermissionsParser::AddAPIPermission(Extension* extension,
309 APIPermission* permission) {
310 DCHECK(extension->permissions_parser());
311 extension->permissions_parser()->initial_required_permissions_
312 ->api_permissions.insert(permission);
313 }
314
315 // static
316 bool PermissionsParser::HasAPIPermission(const Extension* extension,
317 APIPermission::ID permission) {
318 DCHECK(extension->permissions_parser());
319 return extension->permissions_parser()
320 ->initial_required_permissions_->api_permissions.count(permission) > 0;
321 }
322
323 // static
324 void PermissionsParser::SetScriptableHosts(
325 Extension* extension, const URLPatternSet& scriptable_hosts) {
326 DCHECK(extension->permissions_parser());
327 extension->permissions_parser()->initial_required_permissions_
328 ->scriptable_hosts = scriptable_hosts;
329 }
330
331 // static
332 scoped_refptr<const PermissionSet> PermissionsParser::GetRequiredPermissions(
333 const Extension* extension) {
334 DCHECK(extension->GetManifestData(keys::kPermissions));
335 return static_cast<const ManifestPermissions*>(
336 extension->GetManifestData(keys::kPermissions))->permissions;
337 }
338
339 // static
340 scoped_refptr<const PermissionSet> PermissionsParser::GetOptionalPermissions(
341 const Extension* extension) {
342 DCHECK(extension->GetManifestData(keys::kOptionalPermissions));
343 return static_cast<const ManifestPermissions*>(
344 extension->GetManifestData(keys::kOptionalPermissions))->permissions;
345 }
346
347 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698