зеркало из https://github.com/mozilla/gecko-dev.git
358 строки
11 KiB
JavaScript
358 строки
11 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const Ci = Components.interfaces;
|
|
const Cu = Components.utils;
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/AppsUtils.jsm");
|
|
Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
|
|
Cu.import("resource://gre/modules/PermissionsTable.jsm");
|
|
Cu.import("resource://gre/modules/PermissionSettings.jsm");
|
|
|
|
XPCOMUtils.defineLazyServiceGetter(this, "dataStoreService",
|
|
"@mozilla.org/datastore-service;1",
|
|
"nsIDataStoreService");
|
|
|
|
this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker",
|
|
"SystemMessagePermissionsTable"];
|
|
|
|
function debug(aStr) {
|
|
// dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n");
|
|
}
|
|
|
|
// This table maps system message to permission(s), indicating only
|
|
// the system messages granted by the page's permissions are allowed
|
|
// to be registered or sent to that page. Note the empty permission
|
|
// set means this type of system message is always permitted.
|
|
|
|
this.SystemMessagePermissionsTable = {
|
|
"activity": { },
|
|
"alarm": {
|
|
"alarms": []
|
|
},
|
|
"bluetooth-dialer-command": {
|
|
"telephony": []
|
|
},
|
|
"bluetooth-cancel": {
|
|
"bluetooth": []
|
|
},
|
|
"bluetooth-hid-status-changed": {
|
|
"bluetooth": []
|
|
},
|
|
"bluetooth-pairing-request": {
|
|
"bluetooth": []
|
|
},
|
|
"bluetooth-opp-transfer-complete": {
|
|
"bluetooth": []
|
|
},
|
|
"bluetooth-opp-update-progress": {
|
|
"bluetooth": []
|
|
},
|
|
"bluetooth-opp-receiving-file-confirmation": {
|
|
"bluetooth": []
|
|
},
|
|
"bluetooth-opp-transfer-start": {
|
|
"bluetooth": []
|
|
},
|
|
"cellbroadcast-received": {
|
|
"cellbroadcast": []
|
|
},
|
|
"connection": { },
|
|
"captive-portal": {
|
|
"wifi-manage": []
|
|
},
|
|
"dummy-system-message": { }, // for system message testing framework
|
|
"dummy-system-message2": { }, // for system message testing framework
|
|
"headset-button": { },
|
|
"icc-stkcommand": {
|
|
"settings": ["read", "write"]
|
|
},
|
|
"media-button": { },
|
|
"networkstats-alarm": {
|
|
"networkstats-manage": []
|
|
},
|
|
"notification": {
|
|
"desktop-notification": []
|
|
},
|
|
"push": {
|
|
"push": []
|
|
},
|
|
"push-register": {
|
|
"push": []
|
|
},
|
|
"request-sync": { },
|
|
"sms-delivery-success": {
|
|
"sms": []
|
|
},
|
|
"sms-delivery-error": {
|
|
"sms": []
|
|
},
|
|
"sms-read-success": {
|
|
"sms": []
|
|
},
|
|
"sms-received": {
|
|
"sms": []
|
|
},
|
|
"sms-sent": {
|
|
"sms": []
|
|
},
|
|
"sms-failed": {
|
|
"sms": []
|
|
},
|
|
"telephony-new-call": {
|
|
"telephony": []
|
|
},
|
|
"telephony-call-ended": {
|
|
"telephony": []
|
|
},
|
|
"ussd-received": {
|
|
"mobileconnection": []
|
|
},
|
|
"wappush-received": {
|
|
"wappush": []
|
|
},
|
|
"cdma-info-rec-received": {
|
|
"mobileconnection": []
|
|
},
|
|
"nfc-hci-event-transaction": {
|
|
"nfc-hci-events": []
|
|
},
|
|
"nfc-manager-tech-discovered": {
|
|
"nfc-manager": []
|
|
},
|
|
"nfc-manager-tech-lost": {
|
|
"nfc-manager": []
|
|
},
|
|
"nfc-manager-send-file": {
|
|
"nfc-manager": []
|
|
},
|
|
"wifip2p-pairing-request": {
|
|
"wifi-manage": []
|
|
},
|
|
"first-run-with-sim": {
|
|
"settings": ["read", "write"]
|
|
}
|
|
};
|
|
|
|
|
|
this.SystemMessagePermissionsChecker = {
|
|
/**
|
|
* Return all the needed permission names for the given system message.
|
|
* @param string aSysMsgName
|
|
* The system messsage name.
|
|
* @returns object
|
|
* Format: { permName (string): permNamesWithAccess (string array), ... }
|
|
* Ex, { "settings": ["settings-read", "settings-write"], ... }.
|
|
* Note: an empty object will be returned if it's always permitted.
|
|
* @returns null
|
|
* Return and report error when any unexpected error is ecountered.
|
|
* Ex, when the system message we want to search is not included.
|
|
**/
|
|
getSystemMessagePermissions: function getSystemMessagePermissions(aSysMsgName) {
|
|
debug("getSystemMessagePermissions(): aSysMsgName: " + aSysMsgName);
|
|
|
|
let permNames = SystemMessagePermissionsTable[aSysMsgName];
|
|
if (permNames === undefined) {
|
|
debug("'" + aSysMsgName + "' is not associated with permissions. " +
|
|
"Please add them to the SystemMessage[Prefix]PermissionsTable.");
|
|
return null;
|
|
}
|
|
|
|
let object = { };
|
|
for (let permName in permNames) {
|
|
if (PermissionsTable[permName] === undefined) {
|
|
debug("'" + permName + "' for '" + aSysMsgName + "' is invalid. " +
|
|
"Please correct it in the SystemMessage[Prefix]PermissionsTable.");
|
|
return null;
|
|
}
|
|
|
|
// Construct a new permission name array by adding the access suffixes.
|
|
let access = permNames[permName];
|
|
if (!access || !Array.isArray(access)) {
|
|
debug("'" + permName + "' is not associated with access array. " +
|
|
"Please correct it in the SystemMessage[Prefix]PermissionsTable.");
|
|
return null;
|
|
}
|
|
object[permName] = appendAccessToPermName(permName, access);
|
|
}
|
|
return object
|
|
},
|
|
|
|
/**
|
|
* Check if the message is a datastore-update message
|
|
* @param string aSysMsgName
|
|
* The system messsage name.
|
|
*/
|
|
isDataStoreSystemMessage: function(aSysMsgName) {
|
|
return aSysMsgName.indexOf('datastore-update-') === 0;
|
|
},
|
|
|
|
/**
|
|
* Check if this manifest can deliver this particular datastore message.
|
|
*/
|
|
canDeliverDataStoreSystemMessage: function(aSysMsgName, aManifestURL) {
|
|
let store = aSysMsgName.substr('datastore-update-'.length);
|
|
|
|
// Get all the manifest URLs of the apps which can access the datastore.
|
|
let manifestURLs = dataStoreService.getAppManifestURLsForDataStore(store);
|
|
let enumerate = manifestURLs.enumerate();
|
|
while (enumerate.hasMoreElements()) {
|
|
let manifestURL = enumerate.getNext().QueryInterface(Ci.nsISupportsString);
|
|
if (manifestURL == aManifestURL) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Check if the system message is permitted to be registered for the given
|
|
* app at start-up based on the permissions claimed in the app's manifest.
|
|
* @param string aSysMsgName
|
|
* The system messsage name.
|
|
* @param string aManifestURL
|
|
* The app's manifest URL.
|
|
* @param object aManifest
|
|
* The app's manifest.
|
|
* @returns bool
|
|
* Is permitted or not.
|
|
**/
|
|
isSystemMessagePermittedToRegister:
|
|
function isSystemMessagePermittedToRegister(aSysMsgName,
|
|
aManifestURL,
|
|
aManifest) {
|
|
debug("isSystemMessagePermittedToRegister(): " +
|
|
"aSysMsgName: " + aSysMsgName + ", " +
|
|
"aManifestURL: " + aManifestURL + ", " +
|
|
"aManifest: " + JSON.stringify(aManifest));
|
|
|
|
if (this.isDataStoreSystemMessage(aSysMsgName) &&
|
|
this.canDeliverDataStoreSystemMessage(aSysMsgName, aManifestURL)) {
|
|
return true;
|
|
}
|
|
|
|
let permNames = this.getSystemMessagePermissions(aSysMsgName);
|
|
if (permNames === null) {
|
|
return false;
|
|
}
|
|
|
|
// Check to see if the 'webapp' is app/privileged/certified.
|
|
let appStatus;
|
|
switch (AppsUtils.getAppManifestStatus(aManifest)) {
|
|
case Ci.nsIPrincipal.APP_STATUS_CERTIFIED:
|
|
appStatus = "certified";
|
|
break;
|
|
case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED:
|
|
appStatus = "privileged";
|
|
break;
|
|
case Ci.nsIPrincipal.APP_STATUS_INSTALLED:
|
|
appStatus = "app";
|
|
if (aManifest.type == "trusted") {
|
|
appStatus = "trusted";
|
|
}
|
|
break;
|
|
default:
|
|
throw new Error("SystemMessagePermissionsChecker.jsm: " +
|
|
"Cannot decide the app's status. Install cancelled.");
|
|
break;
|
|
}
|
|
|
|
// It's ok here to not pass the origin to ManifestHelper since we only
|
|
// need the permission property and that doesn't depend on uri resolution.
|
|
let newManifest = new ManifestHelper(aManifest, aManifestURL, aManifestURL);
|
|
|
|
for (let permName in permNames) {
|
|
// The app doesn't claim valid permissions for this sytem message.
|
|
if (!newManifest.permissions || !newManifest.permissions[permName]) {
|
|
debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
|
|
"Please add the permission for app: '" + aManifestURL + "'.");
|
|
return false;
|
|
}
|
|
let permValue = PermissionsTable[permName][appStatus];
|
|
if (permValue != Ci.nsIPermissionManager.PROMPT_ACTION &&
|
|
permValue != Ci.nsIPermissionManager.ALLOW_ACTION) {
|
|
debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
|
|
"Please add the permission for app: '" + aManifestURL + "'.");
|
|
return false;
|
|
}
|
|
|
|
// Compare the expanded permission names between the ones in
|
|
// app's manifest and the ones needed for system message.
|
|
let expandedPermNames =
|
|
expandPermissions(permName,
|
|
newManifest.permissions[permName].access);
|
|
|
|
let permNamesWithAccess = permNames[permName];
|
|
|
|
// Early return false as soon as any permission is not matched.
|
|
for (let idx in permNamesWithAccess) {
|
|
let index = expandedPermNames.indexOf(permNamesWithAccess[idx]);
|
|
if (index == -1) {
|
|
debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
|
|
"Please add the permission for app: '" + aOrigin + "'.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// All the permissions needed for this system message are matched.
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Check if the system message is permitted to be sent to the given
|
|
* app's page at run-time based on the current app's permissions.
|
|
* @param string aSysMsgName
|
|
* The system messsage name.
|
|
* @param string aPageURL
|
|
* The app's page URL.
|
|
* @param string aManifestURL
|
|
* The app's manifest URL.
|
|
* @returns bool
|
|
* Is permitted or not.
|
|
**/
|
|
isSystemMessagePermittedToSend:
|
|
function isSystemMessagePermittedToSend(aSysMsgName, aPageURL, aManifestURL) {
|
|
debug("isSystemMessagePermittedToSend(): " +
|
|
"aSysMsgName: " + aSysMsgName + ", " +
|
|
"aPageURL: " + aPageURL + ", " +
|
|
"aManifestURL: " + aManifestURL);
|
|
|
|
if (this.isDataStoreSystemMessage(aSysMsgName) &&
|
|
this.canDeliverDataStoreSystemMessage(aSysMsgName, aManifestURL)) {
|
|
return true;
|
|
}
|
|
|
|
let permNames = this.getSystemMessagePermissions(aSysMsgName);
|
|
if (permNames === null) {
|
|
return false;
|
|
}
|
|
|
|
let pageURI = Services.io.newURI(aPageURL, null, null);
|
|
for (let permName in permNames) {
|
|
let permNamesWithAccess = permNames[permName];
|
|
|
|
// Early return false as soon as any permission is not matched.
|
|
for (let idx in permNamesWithAccess) {
|
|
if(PermissionSettingsModule.getPermission(permNamesWithAccess[idx],
|
|
aManifestURL,
|
|
pageURI.prePath,
|
|
false) != "allow") {
|
|
debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
|
|
"Please add the permission for app: '" + pageURI.prePath + "'.");
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// All the permissions needed for this system message are matched.
|
|
return true;
|
|
}
|
|
};
|