Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 "use strict";
7 const Ci = Components.interfaces;
8 const Cu = Components.utils;
10 this.EXPORTED_SYMBOLS = [
11 "PermissionsTable",
12 "PermissionsReverseTable",
13 "expandPermissions",
14 "appendAccessToPermName",
15 "isExplicitInPermissionsTable"
16 ];
18 // Permission access flags
19 const READONLY = "readonly";
20 const CREATEONLY = "createonly";
21 const READCREATE = "readcreate";
22 const READWRITE = "readwrite";
24 const UNKNOWN_ACTION = Ci.nsIPermissionManager.UNKNOWN_ACTION;
25 const ALLOW_ACTION = Ci.nsIPermissionManager.ALLOW_ACTION;
26 const DENY_ACTION = Ci.nsIPermissionManager.DENY_ACTION;
27 const PROMPT_ACTION = Ci.nsIPermissionManager.PROMPT_ACTION;
29 // Permissions Matrix: https://docs.google.com/spreadsheet/ccc?key=0Akyz_Bqjgf5pdENVekxYRjBTX0dCXzItMnRyUU1RQ0E#gid=0
31 // Permissions that are implicit:
32 // battery-status, network-information, vibration,
33 // device-capabilities
35 this.PermissionsTable = { geolocation: {
36 app: PROMPT_ACTION,
37 privileged: PROMPT_ACTION,
38 certified: PROMPT_ACTION
39 },
40 camera: {
41 app: DENY_ACTION,
42 privileged: PROMPT_ACTION,
43 certified: ALLOW_ACTION
44 },
45 alarms: {
46 app: ALLOW_ACTION,
47 privileged: ALLOW_ACTION,
48 certified: ALLOW_ACTION
49 },
50 "tcp-socket": {
51 app: DENY_ACTION,
52 privileged: ALLOW_ACTION,
53 certified: ALLOW_ACTION
54 },
55 "network-events": {
56 app: DENY_ACTION,
57 privileged: DENY_ACTION,
58 certified: ALLOW_ACTION
59 },
60 contacts: {
61 app: DENY_ACTION,
62 privileged: PROMPT_ACTION,
63 certified: ALLOW_ACTION,
64 access: ["read", "write", "create"]
65 },
66 "device-storage:apps": {
67 app: DENY_ACTION,
68 privileged: DENY_ACTION,
69 certified: ALLOW_ACTION,
70 access: ["read"]
71 },
72 "device-storage:crashes": {
73 app: DENY_ACTION,
74 privileged: DENY_ACTION,
75 certified: ALLOW_ACTION,
76 access: ["read"]
77 },
78 "device-storage:pictures": {
79 app: DENY_ACTION,
80 privileged: PROMPT_ACTION,
81 certified: ALLOW_ACTION,
82 access: ["read", "write", "create"]
83 },
84 "device-storage:videos": {
85 app: DENY_ACTION,
86 privileged: PROMPT_ACTION,
87 certified: ALLOW_ACTION,
88 access: ["read", "write", "create"]
89 },
90 "device-storage:music": {
91 app: DENY_ACTION,
92 privileged: PROMPT_ACTION,
93 certified: ALLOW_ACTION,
94 access: ["read", "write", "create"]
95 },
96 "device-storage:sdcard": {
97 app: DENY_ACTION,
98 privileged: PROMPT_ACTION,
99 certified: ALLOW_ACTION,
100 access: ["read", "write", "create"]
101 },
102 sms: {
103 app: DENY_ACTION,
104 privileged: DENY_ACTION,
105 certified: ALLOW_ACTION
106 },
107 telephony: {
108 app: DENY_ACTION,
109 privileged: DENY_ACTION,
110 certified: ALLOW_ACTION
111 },
112 browser: {
113 app: DENY_ACTION,
114 privileged: ALLOW_ACTION,
115 certified: ALLOW_ACTION
116 },
117 bluetooth: {
118 app: DENY_ACTION,
119 privileged: DENY_ACTION,
120 certified: ALLOW_ACTION
121 },
122 mobileconnection: {
123 app: DENY_ACTION,
124 privileged: DENY_ACTION,
125 certified: ALLOW_ACTION
126 },
127 mobilenetwork: {
128 app: DENY_ACTION,
129 privileged: ALLOW_ACTION,
130 certified: ALLOW_ACTION
131 },
132 power: {
133 app: DENY_ACTION,
134 privileged: DENY_ACTION,
135 certified: ALLOW_ACTION
136 },
137 push: {
138 app: ALLOW_ACTION,
139 privileged: ALLOW_ACTION,
140 certified: ALLOW_ACTION
141 },
142 settings: {
143 app: DENY_ACTION,
144 privileged: DENY_ACTION,
145 certified: ALLOW_ACTION,
146 access: ["read", "write"],
147 additional: ["indexedDB-chrome-settings"]
148 },
149 permissions: {
150 app: DENY_ACTION,
151 privileged: DENY_ACTION,
152 certified: ALLOW_ACTION
153 },
154 phonenumberservice: {
155 app: DENY_ACTION,
156 privileged: DENY_ACTION,
157 certified: ALLOW_ACTION
158 },
159 fmradio: {
160 app: DENY_ACTION,
161 privileged: ALLOW_ACTION,
162 certified: ALLOW_ACTION
163 },
164 attention: {
165 app: DENY_ACTION,
166 privileged: DENY_ACTION,
167 certified: ALLOW_ACTION
168 },
169 "webapps-manage": {
170 app: DENY_ACTION,
171 privileged: DENY_ACTION,
172 certified: ALLOW_ACTION
173 },
174 "backgroundservice": {
175 app: DENY_ACTION,
176 privileged: DENY_ACTION,
177 certified: ALLOW_ACTION
178 },
179 "desktop-notification": {
180 app: ALLOW_ACTION,
181 privileged: ALLOW_ACTION,
182 certified: ALLOW_ACTION
183 },
184 "networkstats-manage": {
185 app: DENY_ACTION,
186 privileged: DENY_ACTION,
187 certified: ALLOW_ACTION
188 },
189 "wifi-manage": {
190 app: DENY_ACTION,
191 privileged: DENY_ACTION,
192 certified: ALLOW_ACTION
193 },
194 "systemXHR": {
195 app: DENY_ACTION,
196 privileged: ALLOW_ACTION,
197 certified: ALLOW_ACTION
198 },
199 "voicemail": {
200 app: DENY_ACTION,
201 privileged: DENY_ACTION,
202 certified: ALLOW_ACTION
203 },
204 "deprecated-hwvideo": {
205 app: DENY_ACTION,
206 privileged: DENY_ACTION,
207 certified: ALLOW_ACTION
208 },
209 "idle": {
210 app: DENY_ACTION,
211 privileged: DENY_ACTION,
212 certified: ALLOW_ACTION
213 },
214 "time": {
215 app: DENY_ACTION,
216 privileged: DENY_ACTION,
217 certified: ALLOW_ACTION
218 },
219 "embed-apps": {
220 app: DENY_ACTION,
221 privileged: DENY_ACTION,
222 certified: ALLOW_ACTION
223 },
224 "storage": {
225 app: ALLOW_ACTION,
226 privileged: ALLOW_ACTION,
227 certified: ALLOW_ACTION,
228 substitute: [
229 "indexedDB-unlimited",
230 "default-persistent-storage"
231 ]
232 },
233 "background-sensors": {
234 app: DENY_ACTION,
235 privileged: DENY_ACTION,
236 certified: ALLOW_ACTION
237 },
238 cellbroadcast: {
239 app: DENY_ACTION,
240 privileged: DENY_ACTION,
241 certified: ALLOW_ACTION
242 },
243 "audio-channel-normal": {
244 app: ALLOW_ACTION,
245 privileged: ALLOW_ACTION,
246 certified: ALLOW_ACTION
247 },
248 "audio-channel-content": {
249 app: ALLOW_ACTION,
250 privileged: ALLOW_ACTION,
251 certified: ALLOW_ACTION
252 },
253 "audio-channel-notification": {
254 app: DENY_ACTION,
255 privileged: ALLOW_ACTION,
256 certified: ALLOW_ACTION
257 },
258 "audio-channel-alarm": {
259 app: DENY_ACTION,
260 privileged: ALLOW_ACTION,
261 certified: ALLOW_ACTION
262 },
263 "audio-channel-telephony": {
264 app: DENY_ACTION,
265 privileged: DENY_ACTION,
266 certified: ALLOW_ACTION
267 },
268 "audio-channel-ringer": {
269 app: DENY_ACTION,
270 privileged: DENY_ACTION,
271 certified: ALLOW_ACTION
272 },
273 "audio-channel-publicnotification": {
274 app: DENY_ACTION,
275 privileged: DENY_ACTION,
276 certified: ALLOW_ACTION
277 },
278 "open-remote-window": {
279 app: DENY_ACTION,
280 privileged: DENY_ACTION,
281 certified: ALLOW_ACTION
282 },
283 "input": {
284 app: DENY_ACTION,
285 privileged: ALLOW_ACTION,
286 certified: ALLOW_ACTION
287 },
288 "input-manage": {
289 app: DENY_ACTION,
290 privileged: DENY_ACTION,
291 certified: ALLOW_ACTION
292 },
293 "wappush": {
294 app: DENY_ACTION,
295 privileged: DENY_ACTION,
296 certified: ALLOW_ACTION
297 },
298 "audio-capture": {
299 app: PROMPT_ACTION,
300 privileged: PROMPT_ACTION,
301 certified: PROMPT_ACTION
302 },
303 "nfc": {
304 app: DENY_ACTION,
305 privileged: DENY_ACTION,
306 certified: ALLOW_ACTION,
307 access: ["read", "write"]
308 },
309 "nfc-manager": {
310 app: DENY_ACTION,
311 privileged: DENY_ACTION,
312 certified: ALLOW_ACTION
313 },
314 "speaker-control": {
315 app: DENY_ACTION,
316 privileged: ALLOW_ACTION,
317 certified: ALLOW_ACTION
318 },
319 "downloads": {
320 app: DENY_ACTION,
321 privileged: DENY_ACTION,
322 certified: ALLOW_ACTION
323 },
324 "video-capture": {
325 app: PROMPT_ACTION,
326 privileged: PROMPT_ACTION,
327 certified: PROMPT_ACTION
328 },
329 };
331 /**
332 * Append access modes to the permission name as suffixes.
333 * e.g. permission name 'contacts' with ['read', 'write'] =
334 * ['contacts-read', contacts-write']
335 * @param string aPermName
336 * @param array aAccess
337 * @returns array containing access-appended permission names.
338 **/
339 this.appendAccessToPermName = function appendAccessToPermName(aPermName, aAccess) {
340 if (aAccess.length == 0) {
341 return [aPermName];
342 }
343 return aAccess.map(function(aMode) {
344 return aPermName + "-" + aMode;
345 });
346 };
348 /**
349 * Expand an access string into multiple permission names,
350 * e.g: permission name 'contacts' with 'readwrite' =
351 * ['contacts-read', 'contacts-create', 'contacts-write']
352 * @param string aPermName
353 * @param string aAccess (optional)
354 * @returns array containing expanded permission names.
355 **/
356 this.expandPermissions = function expandPermissions(aPermName, aAccess) {
357 if (!PermissionsTable[aPermName]) {
358 let errorMsg =
359 "PermissionsTable.jsm: expandPermissions: Unknown Permission: " + aPermName;
360 Cu.reportError(errorMsg);
361 dump(errorMsg);
362 return [];
363 }
365 const tableEntry = PermissionsTable[aPermName];
367 if (tableEntry.substitute && tableEntry.additional) {
368 let errorMsg =
369 "PermissionsTable.jsm: expandPermissions: Can't handle both 'substitute' " +
370 "and 'additional' entries for permission: " + aPermName;
371 Cu.reportError(errorMsg);
372 dump(errorMsg);
373 return [];
374 }
376 if (!aAccess && tableEntry.access ||
377 aAccess && !tableEntry.access) {
378 let errorMsg =
379 "PermissionsTable.jsm: expandPermissions: Invalid access for permission " +
380 aPermName + ": " + aAccess + "\n";
381 Cu.reportError(errorMsg);
382 dump(errorMsg);
383 return [];
384 }
386 let expandedPermNames = [];
388 if (tableEntry.access && aAccess) {
389 let requestedSuffixes = [];
390 switch (aAccess) {
391 case READONLY:
392 requestedSuffixes.push("read");
393 break;
394 case CREATEONLY:
395 requestedSuffixes.push("create");
396 break;
397 case READCREATE:
398 requestedSuffixes.push("read", "create");
399 break;
400 case READWRITE:
401 requestedSuffixes.push("read", "create", "write");
402 break;
403 default:
404 return [];
405 }
407 let permArr = appendAccessToPermName(aPermName, requestedSuffixes);
409 // Add the same suffix to each of the additions.
410 if (tableEntry.additional) {
411 for each (let additional in tableEntry.additional) {
412 permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes));
413 }
414 }
416 // Only add the suffixed version if the suffix exists in the table.
417 for (let idx in permArr) {
418 let suffix = requestedSuffixes[idx % requestedSuffixes.length];
419 if (tableEntry.access.indexOf(suffix) != -1) {
420 expandedPermNames.push(permArr[idx]);
421 }
422 }
423 } else if (tableEntry.substitute) {
424 expandedPermNames = expandedPermNames.concat(tableEntry.substitute);
425 } else {
426 expandedPermNames.push(aPermName);
427 // Include each of the additions exactly as they appear in the table.
428 if (tableEntry.additional) {
429 expandedPermNames = expandedPermNames.concat(tableEntry.additional);
430 }
431 }
433 return expandedPermNames;
434 };
436 this.PermissionsReverseTable = (function () {
437 // PermissionsTable as it is works well for direct searches, but not
438 // so well for reverse ones (that is, if I get something like
439 // device-storage:music-read or indexedDB-chrome-settings-read how
440 // do I know which permission it really is? Hence this table is
441 // born. The idea is that
442 // reverseTable[device-storage:music-read] should return
443 // device-storage:music
444 let reverseTable = {};
446 for (let permName in PermissionsTable) {
447 let permAliases;
448 if (PermissionsTable[permName].access) {
449 permAliases = expandPermissions(permName, "readwrite");
450 } else {
451 permAliases = expandPermissions(permName);
452 }
453 for (let i = 0; i < permAliases.length; i++) {
454 reverseTable[permAliases[i]] = permName;
455 }
456 }
458 return reverseTable;
460 })();
462 this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) {
464 // Check to see if the 'webapp' is app/privileged/certified.
465 let appStatus;
466 switch (aIntStatus) {
467 case Ci.nsIPrincipal.APP_STATUS_CERTIFIED:
468 appStatus = "certified";
469 break;
470 case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED:
471 appStatus = "privileged";
472 break;
473 default: // If it isn't certified or privileged, it's app
474 appStatus = "app";
475 break;
476 }
478 let realPerm = PermissionsReverseTable[aPermName];
480 if (realPerm) {
481 return (PermissionsTable[realPerm][appStatus] ==
482 Ci.nsIPermissionManager.PROMPT_ACTION);
483 } else {
484 return false;
485 }
486 }