gecko/dom/messages/SystemMessagePermissionsChecker.jsm
Phil Ringnalda 5e00245a97 Backed out 7 changesets (bug 1018320) because the debug M12 is still from this
Backed out changeset f60d4ad64070 (bug 1018320)
Backed out changeset 1aac4d23ccd2 (bug 1018320)
Backed out changeset 6063a2463d6c (bug 1018320)
Backed out changeset 2df23c44ef44 (bug 1018320)
Backed out changeset daf456b0a23a (bug 1018320)
Backed out changeset 3e2e4d298e98 (bug 1018320)
Backed out changeset 012373bb552b (bug 1018320)
2015-01-05 08:02:54 -08:00

348 lines
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
"headset-button": { },
"icc-stkcommand": {
"settings": ["read", "write"]
},
"media-button": { },
"networkstats-alarm": {
"networkstats-manage": []
},
"notification": {
"desktop-notification": []
},
"push": {
"push": []
},
"push-register": {
"push": []
},
"sms-delivery-success": {
"sms": []
},
"sms-read-success": {
"sms": []
},
"sms-received": {
"sms": []
},
"sms-sent": {
"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": { },
"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;
}
};