Bug 846200 - Support for granting settings permissions on a per-permission basis; r=bent

This commit is contained in:
Kyle Machulis 2014-08-19 13:43:56 -07:00
Родитель 0ff3e02337
Коммит 9ef69caded
4 изменённых файлов: 72 добавлений и 51 удалений

Просмотреть файл

@ -30,20 +30,6 @@ function debug(aMsg) {
//dump("-*-*- PermissionsInstaller.jsm : " + aMsg + "\n");
}
// An array carring all the possible (expanded) permission names.
let AllPossiblePermissions = [];
for (let permName in PermissionsTable) {
let expandedPermNames = [];
if (PermissionsTable[permName].access) {
expandedPermNames = expandPermissions(permName, READWRITE);
} else {
expandedPermNames = expandPermissions(permName);
}
AllPossiblePermissions = AllPossiblePermissions.concat(expandedPermNames);
AllPossiblePermissions =
AllPossiblePermissions.concat(["offline-app", "pin-app"]);
}
this.PermissionsInstaller = {
/**
* Install permissisions or remove deprecated permissions upon re-install.

Просмотреть файл

@ -12,7 +12,8 @@ this.EXPORTED_SYMBOLS = [
"PermissionsReverseTable",
"expandPermissions",
"appendAccessToPermName",
"isExplicitInPermissionsTable"
"isExplicitInPermissionsTable",
"AllPossiblePermissions"
];
// Permission access flags
@ -144,7 +145,7 @@ this.PermissionsTable = { geolocation: {
privileged: DENY_ACTION,
certified: ALLOW_ACTION,
access: ["read", "write"],
additional: ["indexedDB-chrome-settings"]
additional: ["indexedDB-chrome-settings", "settings-api"]
},
permissions: {
app: DENY_ACTION,
@ -491,7 +492,10 @@ this.expandPermissions = function expandPermissions(aPermName, aAccess) {
return expandedPermNames;
};
this.PermissionsReverseTable = (function () {
this.PermissionsReverseTable = {};
this.AllPossiblePermissions = [];
(function () {
// PermissionsTable as it is works well for direct searches, but not
// so well for reverse ones (that is, if I get something like
// device-storage:music-read or indexedDB-chrome-settings-read how
@ -499,8 +503,9 @@ this.PermissionsReverseTable = (function () {
// born. The idea is that
// reverseTable[device-storage:music-read] should return
// device-storage:music
let reverseTable = {};
//
// We also need a list of all the possible permissions for things like the
// settingsmanager, so construct that while we're at it.
for (let permName in PermissionsTable) {
let permAliases;
if (PermissionsTable[permName].access) {
@ -509,12 +514,12 @@ this.PermissionsReverseTable = (function () {
permAliases = expandPermissions(permName);
}
for (let i = 0; i < permAliases.length; i++) {
reverseTable[permAliases[i]] = permName;
PermissionsReverseTable[permAliases[i]] = permName;
AllPossiblePermissions.push(permAliases[i]);
}
}
return reverseTable;
AllPossiblePermissions =
AllPossiblePermissions.concat(["offline-app", "pin-app"]);
})();
this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) {

Просмотреть файл

@ -2109,8 +2109,8 @@ Navigator::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
}
if (name.EqualsLiteral("mozSettings")) {
bool hasPermission = CheckPermission("settings-read") ||
CheckPermission("settings-write");
bool hasPermission = CheckPermission("settings-api-read") ||
CheckPermission("settings-api-write");
if (!hasPermission) {
FillPropertyDescriptor(aDesc, aObject, JS::NullValue(), false);
return true;

Просмотреть файл

@ -17,6 +17,7 @@ Cu.import("resource://gre/modules/SettingsQueue.jsm");
Cu.import("resource://gre/modules/SettingsDB.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PermissionsTable.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
@ -66,7 +67,7 @@ SettingsLock.prototype = {
this._open = false;
}.bind(lock);
clearReq.onerror = function() {
Services.DOMRequest.fireError(request, 0)
Services.DOMRequest.fireError(request, 0);
};
break;
case "set":
@ -105,7 +106,7 @@ SettingsLock.prototype = {
setReq.onerror = function() {
if (!request.error) {
Services.DOMRequest.fireError(request, setReq.error.name)
Services.DOMRequest.fireError(request, setReq.error.name);
}
};
@ -118,7 +119,7 @@ SettingsLock.prototype = {
};
checkKeyRequest.onerror = function(event) {
if (!request.error) {
Services.DOMRequest.fireError(request, checkKeyRequest.error.name)
Services.DOMRequest.fireError(request, checkKeyRequest.error.name);
}
};
}
@ -152,7 +153,7 @@ SettingsLock.prototype = {
}.bind(lock);
getReq.onerror = function() {
Services.DOMRequest.fireError(request, 0)
Services.DOMRequest.fireError(request, 0);
};
break;
}
@ -163,7 +164,7 @@ SettingsLock.prototype = {
if (DEBUG) debug("database opened, creating transaction");
let manager = this._settingsManager;
let transactionType = manager.hasWritePrivileges ? "readwrite" : "readonly";
let transactionType = manager.hasAnyWritePrivileges ? "readwrite" : "readonly";
this._transaction =
manager._settingsDB._db.transaction(SETTINGSSTORE_NAME, transactionType);
@ -183,15 +184,14 @@ SettingsLock.prototype = {
throw Components.results.NS_ERROR_ABORT;
}
if (this._settingsManager.hasReadPrivileges) {
if (!this._settingsManager.hasReadPermission("settings:" + aName)) {
if (DEBUG) debug("get not allowed");
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
this._requests.enqueue({ request: req, intent:"get", name: aName });
this.maybeProcess();
return req;
} else {
if (DEBUG) debug("get not allowed");
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
},
_serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
@ -232,25 +232,28 @@ SettingsLock.prototype = {
throw "Settings lock not open";
}
if (this._settingsManager.hasWritePrivileges) {
// Check each requested permission to make sure we can set it
let keys = Object.getOwnPropertyNames(aSettings);
for (let i = 0; i < keys.length; i++) {
if (!this._settingsManager.hasWritePermission("settings:" + keys[i])) {
if (DEBUG) debug("set not allowed on" + keys[i]);
throw "No permission to call set on " + keys[i];
}
}
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
if (DEBUG) debug("send: " + JSON.stringify(aSettings));
let settings = this._serializePreservingBinaries(aSettings);
this._requests.enqueue({request: req, intent: "set", settings: settings});
this.maybeProcess();
return req;
} else {
if (DEBUG) debug("set not allowed");
throw "No permission to call set";
}
},
clear: function clear() {
if (!this._open) {
throw "Settings lock not open";
}
if (this._settingsManager.hasWritePrivileges) {
// Only certified apps should be allowed to clear
if (this._settingsManager.hasFullWritePrivileges) {
let req = Services.DOMRequest.createRequest(this._settingsManager._window);
this._requests.enqueue({ request: req, intent: "clear"});
this.maybeProcess();
@ -273,6 +276,7 @@ function SettingsManager() {
SettingsManager.prototype = {
_callbacks: null,
_perms: [],
_wrap: function _wrap(obj) {
return Cu.cloneInto(obj, this._window);
@ -340,9 +344,9 @@ SettingsManager.prototype = {
removeObserver: function removeObserver(aName, aCallback) {
if (DEBUG) debug("deleteObserver " + aName);
if (this._callbacks && this._callbacks[aName]) {
let index = this._callbacks[aName].indexOf(aCallback)
let index = this._callbacks[aName].indexOf(aCallback);
if (index != -1) {
this._callbacks[aName].splice(index, 1)
this._callbacks[aName].splice(index, 1);
} else {
if (DEBUG) debug("Callback not found for: " + aName);
}
@ -351,6 +355,14 @@ SettingsManager.prototype = {
}
},
hasReadPermission: function hasPermission(aName) {
return this.hasFullReadPrivileges || this._perms.indexOf(aName + "-read") != -1;
},
hasWritePermission: function hasPermission(aName) {
return this.hasFullWritePrivileges || this._perms.indexOf(aName + "-write") != -1;
},
init: function(aWindow) {
mrm.registerStrongReporter(this);
cpmm.addMessageListener("Settings:Change:Return:OK", this);
@ -359,16 +371,34 @@ SettingsManager.prototype = {
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
this.innerWindowID = util.currentInnerWindowID;
let readPerm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "settings-read");
let writePerm = Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "settings-write");
this.hasReadPrivileges = readPerm == Ci.nsIPermissionManager.ALLOW_ACTION;
this.hasWritePrivileges = writePerm == Ci.nsIPermissionManager.ALLOW_ACTION;
if (this.hasReadPrivileges) {
this.hasAnyWritePrivileges = false;
for (let idx in AllPossiblePermissions) {
let permName = AllPossiblePermissions[idx];
//Check to see if this is a settings permission. All settings permissions
//begin with the word settings.
if (permName.indexOf("settings") != 0) {
continue;
}
if (Services.perms.testExactPermissionFromPrincipal(this._window.document.nodePrincipal, permName) == Ci.nsIPermissionManager.ALLOW_ACTION) {
if(permName.indexOf("-write") > 0) {
this.hasAnyWritePrivileges = true;
}
this._perms.push(permName);
}
}
this.hasFullReadPrivileges = this.hasReadPermission("settings");
this.hasFullWritePrivileges = this.hasWritePermission("settings");
if (this.hasFullReadPrivileges) {
cpmm.sendAsyncMessage("Settings:RegisterForMessages");
}
if (!this.hasReadPrivileges && !this.hasWritePrivileges) {
// settings-api is an additional setting on all settings permissions. This
// is how we can figure out whether to expose the mozSettings DOM object.
if (!this.hasReadPermission("settings-api") && !this.hasWritePermission("settings-api")) {
dump("No settings permission for: " + aWindow.document.nodePrincipal.origin + "\n");
Cu.reportError("No settings permission for: " + aWindow.document.nodePrincipal.origin);
}
@ -426,4 +456,4 @@ SettingsManager.prototype = {
Ci.nsIMemoryReporter]),
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsManager, SettingsLock])
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SettingsManager, SettingsLock]);