Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
11 Cu.import("resource://gre/modules/Services.jsm");
12 Cu.import("resource://gre/modules/AppsUtils.jsm");
13 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
14 Cu.import("resource://gre/modules/PermissionsTable.jsm");
15 Cu.import("resource://gre/modules/PermissionSettings.jsm");
17 this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker",
18 "SystemMessagePermissionsTable"];
20 function debug(aStr) {
21 // dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n");
22 }
24 // This table maps system message to permission(s), indicating only
25 // the system messages granted by the page's permissions are allowed
26 // to be registered or sent to that page. Note the empty permission
27 // set means this type of system message is always permitted.
29 this.SystemMessagePermissionsTable = {
30 "activity": { },
31 "alarm": {
32 "alarms": []
33 },
34 "bluetooth-dialer-command": {
35 "telephony": []
36 },
37 "bluetooth-cancel": {
38 "bluetooth": []
39 },
40 "bluetooth-hid-status-changed": {
41 "bluetooth": []
42 },
43 "bluetooth-pairing-request": {
44 "bluetooth": []
45 },
46 "bluetooth-opp-transfer-complete": {
47 "bluetooth": []
48 },
49 "bluetooth-opp-update-progress": {
50 "bluetooth": []
51 },
52 "bluetooth-opp-receiving-file-confirmation": {
53 "bluetooth": []
54 },
55 "bluetooth-opp-transfer-start": {
56 "bluetooth": []
57 },
58 "connection": { },
59 "dummy-system-message": { }, // for system message testing framework
60 "headset-button": { },
61 "icc-stkcommand": {
62 "settings": ["read", "write"]
63 },
64 "media-button": { },
65 "networkstats-alarm": {
66 "networkstats-manage": []
67 },
68 "notification": {
69 "desktop-notification": []
70 },
71 "push": {
72 "push": []
73 },
74 "push-register": {
75 "push": []
76 },
77 "sms-delivery-success": {
78 "sms": []
79 },
80 "sms-read-success": {
81 "sms": []
82 },
83 "sms-received": {
84 "sms": []
85 },
86 "sms-sent": {
87 "sms": []
88 },
89 "telephony-new-call": {
90 "telephony": []
91 },
92 "telephony-call-ended": {
93 "telephony": []
94 },
95 "ussd-received": {
96 "mobileconnection": []
97 },
98 "wappush-received": {
99 "wappush": []
100 },
101 "cdma-info-rec-received": {
102 "mobileconnection": []
103 },
104 "nfc-manager-tech-discovered": {
105 "nfc-manager": []
106 },
107 "nfc-manager-tech-lost": {
108 "nfc-manager": []
109 },
110 "nfc-manager-send-file": {
111 "nfc-manager": []
112 },
113 "nfc-powerlevel-change": {
114 "settings": ["read", "write"]
115 },
116 "wifip2p-pairing-request": { },
117 "first-run-with-sim": {
118 "settings": ["read", "write"]
119 }
120 };
122 this.SystemMessagePermissionsChecker = {
123 /**
124 * Return all the needed permission names for the given system message.
125 * @param string aSysMsgName
126 * The system messsage name.
127 * @returns object
128 * Format: { permName (string): permNamesWithAccess (string array), ... }
129 * Ex, { "settings": ["settings-read", "settings-write"], ... }.
130 * Note: an empty object will be returned if it's always permitted.
131 * @returns null
132 * Return and report error when any unexpected error is ecountered.
133 * Ex, when the system message we want to search is not included.
134 **/
135 getSystemMessagePermissions: function getSystemMessagePermissions(aSysMsgName) {
136 debug("getSystemMessagePermissions(): aSysMsgName: " + aSysMsgName);
138 let permNames = SystemMessagePermissionsTable[aSysMsgName];
139 if (permNames === undefined) {
140 debug("'" + aSysMsgName + "' is not associated with permissions. " +
141 "Please add them to the SystemMessagePermissionsTable.");
142 return null;
143 }
145 let object = { };
146 for (let permName in permNames) {
147 if (PermissionsTable[permName] === undefined) {
148 debug("'" + permName + "' for '" + aSysMsgName + "' is invalid. " +
149 "Please correct it in the SystemMessagePermissionsTable.");
150 return null;
151 }
153 // Construct a new permission name array by adding the access suffixes.
154 let access = permNames[permName];
155 if (!access || !Array.isArray(access)) {
156 debug("'" + permName + "' is not associated with access array. " +
157 "Please correct it in the SystemMessagePermissionsTable.");
158 return null;
159 }
160 object[permName] = appendAccessToPermName(permName, access);
161 }
162 return object
163 },
165 /**
166 * Check if the system message is permitted to be registered for the given
167 * app at start-up based on the permissions claimed in the app's manifest.
168 * @param string aSysMsgName
169 * The system messsage name.
170 * @param string aOrigin
171 * The app's origin.
172 * @param object aManifest
173 * The app's manifest.
174 * @returns bool
175 * Is permitted or not.
176 **/
177 isSystemMessagePermittedToRegister:
178 function isSystemMessagePermittedToRegister(aSysMsgName, aOrigin, aManifest) {
179 debug("isSystemMessagePermittedToRegister(): " +
180 "aSysMsgName: " + aSysMsgName + ", " +
181 "aOrigin: " + aOrigin + ", " +
182 "aManifest: " + JSON.stringify(aManifest));
184 let permNames = this.getSystemMessagePermissions(aSysMsgName);
185 if (permNames === null) {
186 return false;
187 }
189 // Check to see if the 'webapp' is app/privileged/certified.
190 let appStatus;
191 switch (AppsUtils.getAppManifestStatus(aManifest)) {
192 case Ci.nsIPrincipal.APP_STATUS_CERTIFIED:
193 appStatus = "certified";
194 break;
195 case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED:
196 appStatus = "privileged";
197 break;
198 case Ci.nsIPrincipal.APP_STATUS_INSTALLED:
199 appStatus = "app";
200 break;
201 default:
202 throw new Error("SystemMessagePermissionsChecker.jsm: " +
203 "Cannot decide the app's status. Install cancelled.");
204 break;
205 }
207 let newManifest = new ManifestHelper(aManifest, aOrigin);
209 for (let permName in permNames) {
210 // The app doesn't claim valid permissions for this sytem message.
211 if (!newManifest.permissions || !newManifest.permissions[permName]) {
212 debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
213 "Please add the permission for app: '" + aOrigin + "'.");
214 return false;
215 }
216 let permValue = PermissionsTable[permName][appStatus];
217 if (permValue != Ci.nsIPermissionManager.PROMPT_ACTION &&
218 permValue != Ci.nsIPermissionManager.ALLOW_ACTION) {
219 debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
220 "Please add the permission for app: '" + aOrigin + "'.");
221 return false;
222 }
224 // Compare the expanded permission names between the ones in
225 // app's manifest and the ones needed for system message.
226 let expandedPermNames =
227 expandPermissions(permName,
228 newManifest.permissions[permName].access);
230 let permNamesWithAccess = permNames[permName];
232 // Early return false as soon as any permission is not matched.
233 for (let idx in permNamesWithAccess) {
234 let index = expandedPermNames.indexOf(permNamesWithAccess[idx]);
235 if (index == -1) {
236 debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
237 "Please add the permission for app: '" + aOrigin + "'.");
238 return false;
239 }
240 }
241 }
243 // All the permissions needed for this system message are matched.
244 return true;
245 },
247 /**
248 * Check if the system message is permitted to be sent to the given
249 * app's page at run-time based on the current app's permissions.
250 * @param string aSysMsgName
251 * The system messsage name.
252 * @param string aPageURL
253 * The app's page URL.
254 * @param string aManifestURL
255 * The app's manifest URL.
256 * @returns bool
257 * Is permitted or not.
258 **/
259 isSystemMessagePermittedToSend:
260 function isSystemMessagePermittedToSend(aSysMsgName, aPageURL, aManifestURL) {
261 debug("isSystemMessagePermittedToSend(): " +
262 "aSysMsgName: " + aSysMsgName + ", " +
263 "aPageURL: " + aPageURL + ", " +
264 "aManifestURL: " + aManifestURL);
266 let permNames = this.getSystemMessagePermissions(aSysMsgName);
267 if (permNames === null) {
268 return false;
269 }
271 let pageURI = Services.io.newURI(aPageURL, null, null);
272 for (let permName in permNames) {
273 let permNamesWithAccess = permNames[permName];
275 // Early return false as soon as any permission is not matched.
276 for (let idx in permNamesWithAccess) {
277 if(PermissionSettingsModule.getPermission(permNamesWithAccess[idx],
278 aManifestURL,
279 pageURI.prePath,
280 false) != "allow") {
281 debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
282 "Please add the permission for app: '" + pageURI.prePath + "'.");
283 return false;
284 }
285 }
286 }
288 // All the permissions needed for this system message are matched.
289 return true;
290 }
291 };