|
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/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 const Ci = Components.interfaces; |
|
8 const Cu = Components.utils; |
|
9 |
|
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"); |
|
16 |
|
17 this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker", |
|
18 "SystemMessagePermissionsTable"]; |
|
19 |
|
20 function debug(aStr) { |
|
21 // dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n"); |
|
22 } |
|
23 |
|
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. |
|
28 |
|
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 }; |
|
121 |
|
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); |
|
137 |
|
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 } |
|
144 |
|
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 } |
|
152 |
|
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 }, |
|
164 |
|
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)); |
|
183 |
|
184 let permNames = this.getSystemMessagePermissions(aSysMsgName); |
|
185 if (permNames === null) { |
|
186 return false; |
|
187 } |
|
188 |
|
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 } |
|
206 |
|
207 let newManifest = new ManifestHelper(aManifest, aOrigin); |
|
208 |
|
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 } |
|
223 |
|
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); |
|
229 |
|
230 let permNamesWithAccess = permNames[permName]; |
|
231 |
|
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 } |
|
242 |
|
243 // All the permissions needed for this system message are matched. |
|
244 return true; |
|
245 }, |
|
246 |
|
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); |
|
265 |
|
266 let permNames = this.getSystemMessagePermissions(aSysMsgName); |
|
267 if (permNames === null) { |
|
268 return false; |
|
269 } |
|
270 |
|
271 let pageURI = Services.io.newURI(aPageURL, null, null); |
|
272 for (let permName in permNames) { |
|
273 let permNamesWithAccess = permNames[permName]; |
|
274 |
|
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 } |
|
287 |
|
288 // All the permissions needed for this system message are matched. |
|
289 return true; |
|
290 } |
|
291 }; |