location: Add AppAuthorizer class
This class will be responsible for authorizing applications that try to access location information. Since this is mainly targetted for xdg-app applications, we make use of xdg-app's D-Bus API to store per-application authorization. https://bugzilla.gnome.org/show_bug.cgi?id=762119
This commit is contained in:
parent
e98a434ede
commit
34fc454764
@ -17,6 +17,9 @@ const LOCATION_SCHEMA = 'org.gnome.system.location';
|
|||||||
const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
|
const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
|
||||||
const ENABLED = 'enabled';
|
const ENABLED = 'enabled';
|
||||||
|
|
||||||
|
const APP_PERMISSIONS_TABLE = 'desktop';
|
||||||
|
const APP_PERMISSIONS_ID = 'geolocation';
|
||||||
|
|
||||||
const GeoclueAccuracyLevel = {
|
const GeoclueAccuracyLevel = {
|
||||||
NONE: 0,
|
NONE: 0,
|
||||||
COUNTRY: 1,
|
COUNTRY: 1,
|
||||||
@ -26,6 +29,15 @@ const GeoclueAccuracyLevel = {
|
|||||||
EXACT: 8
|
EXACT: 8
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function accuracyLevelToString(accuracyLevel) {
|
||||||
|
for (let key in GeoclueAccuracyLevel) {
|
||||||
|
if (GeoclueAccuracyLevel[key] == accuracyLevel)
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'NONE';
|
||||||
|
}
|
||||||
|
|
||||||
var GeoclueIface = '<node> \
|
var GeoclueIface = '<node> \
|
||||||
<interface name="org.freedesktop.GeoClue2.Manager"> \
|
<interface name="org.freedesktop.GeoClue2.Manager"> \
|
||||||
<property name="InUse" type="b" access="read"/> \
|
<property name="InUse" type="b" access="read"/> \
|
||||||
@ -50,6 +62,24 @@ var AgentIface = '<node> \
|
|||||||
</interface> \
|
</interface> \
|
||||||
</node>';
|
</node>';
|
||||||
|
|
||||||
|
var XdgAppIface = '<node> \
|
||||||
|
<interface name="org.freedesktop.XdgApp.PermissionStore"> \
|
||||||
|
<method name="Lookup"> \
|
||||||
|
<arg name="table" type="s" direction="in"/> \
|
||||||
|
<arg name="id" type="s" direction="in"/> \
|
||||||
|
<arg name="permissions" type="a{sas}" direction="out"/> \
|
||||||
|
<arg name="data" type="v" direction="out"/> \
|
||||||
|
</method> \
|
||||||
|
<method name="Set"> \
|
||||||
|
<arg name="table" type="s" direction="in"/> \
|
||||||
|
<arg name="create" type="b" direction="in"/> \
|
||||||
|
<arg name="id" type="s" direction="in"/> \
|
||||||
|
<arg name="app_permissions" type="a{sas}" direction="in"/> \
|
||||||
|
<arg name="data" type="v" direction="in"/> \
|
||||||
|
</method> \
|
||||||
|
</interface> \
|
||||||
|
</node>';
|
||||||
|
|
||||||
const Indicator = new Lang.Class({
|
const Indicator = new Lang.Class({
|
||||||
Name: 'LocationIndicator',
|
Name: 'LocationIndicator',
|
||||||
Extends: PanelMenu.SystemIndicator,
|
Extends: PanelMenu.SystemIndicator,
|
||||||
@ -222,6 +252,135 @@ function clamp(value, min, max) {
|
|||||||
return Math.max(min, Math.min(max, value));
|
return Math.max(min, Math.min(max, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AppAuthorizer = new Lang.Class({
|
||||||
|
Name: 'LocationAppAuthorizer',
|
||||||
|
|
||||||
|
_init: function(desktopId,
|
||||||
|
reqAccuracyLevel,
|
||||||
|
permStoreProxy,
|
||||||
|
maxAccuracyLevel) {
|
||||||
|
this.desktopId = desktopId;
|
||||||
|
this.reqAccuracyLevel = reqAccuracyLevel;
|
||||||
|
this._permStoreProxy = permStoreProxy;
|
||||||
|
this._maxAccuracyLevel = maxAccuracyLevel;
|
||||||
|
|
||||||
|
this._accuracyLevel = GeoclueAccuracyLevel.NONE;
|
||||||
|
this._timesAllowed = 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
authorize: function(onAuthDone) {
|
||||||
|
this._onAuthDone = onAuthDone;
|
||||||
|
|
||||||
|
let appSystem = Shell.AppSystem.get_default();
|
||||||
|
this._app = appSystem.lookup_app(this.desktopId + ".desktop");
|
||||||
|
if (this._app == null || this._permStoreProxy == null) {
|
||||||
|
this._completeAuth();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._permStoreProxy.LookupRemote(APP_PERMISSIONS_TABLE,
|
||||||
|
APP_PERMISSIONS_ID,
|
||||||
|
Lang.bind(this,
|
||||||
|
this._onPermLookupDone));
|
||||||
|
},
|
||||||
|
|
||||||
|
_onPermLookupDone: function(result, error) {
|
||||||
|
if (error != null) {
|
||||||
|
if (error.domain == Gio.DBusError) {
|
||||||
|
// Likely no xdg-app installed, just authorize the app
|
||||||
|
this._accuracyLevel = this.reqAccuracyLevel;
|
||||||
|
this._permStoreProxy = null;
|
||||||
|
this._completeAuth();
|
||||||
|
} else {
|
||||||
|
// Currently xdg-app throws an error if we lookup for
|
||||||
|
// unknown ID (which would be the case first time this code
|
||||||
|
// runs) so we continue with user authorization as normal
|
||||||
|
// and ID is added to the store if user says "yes".
|
||||||
|
log(error.message);
|
||||||
|
this._permissions = {};
|
||||||
|
this._userAuthorizeApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
[this._permissions] = result;
|
||||||
|
let permission = this._permissions[this.desktopId];
|
||||||
|
|
||||||
|
let [levelStr, timeStr] = permission || ['NONE', '0'];
|
||||||
|
this._accuracyLevel = GeoclueAccuracyLevel[levelStr] ||
|
||||||
|
GeoclueAccuracyLevel.NONE;
|
||||||
|
this._timesAllowed = Number(timeStr) || 0;
|
||||||
|
|
||||||
|
if (this._timesAllowed < 3)
|
||||||
|
this._userAuthorizeApp();
|
||||||
|
else
|
||||||
|
this._completeAuth();
|
||||||
|
},
|
||||||
|
|
||||||
|
_userAuthorizeApp: function() {
|
||||||
|
let name = this._app.get_name();
|
||||||
|
let appInfo = this._app.get_app_info();
|
||||||
|
let reason = appInfo.get_string("X-Geoclue-Reason");
|
||||||
|
|
||||||
|
this._showAppAuthDialog(name, reason);
|
||||||
|
},
|
||||||
|
|
||||||
|
_showAppAuthDialog: function(name, reason) {
|
||||||
|
this._dialog = new GeolocationDialog(name,
|
||||||
|
reason,
|
||||||
|
this.reqAccuracyLevel);
|
||||||
|
|
||||||
|
let responseId = this._dialog.connect('response', Lang.bind(this,
|
||||||
|
function(dialog, level) {
|
||||||
|
this._dialog.disconnect(responseId);
|
||||||
|
this._accuracyLevel = level;
|
||||||
|
this._completeAuth();
|
||||||
|
}));
|
||||||
|
|
||||||
|
this._dialog.open();
|
||||||
|
},
|
||||||
|
|
||||||
|
_completeAuth: function() {
|
||||||
|
if (this._accuracyLevel != GeoclueAccuracyLevel.NONE) {
|
||||||
|
this._accuracyLevel = clamp(this._accuracyLevel,
|
||||||
|
0,
|
||||||
|
this._maxAccuracyLevel);
|
||||||
|
this._timesAllowed++;
|
||||||
|
}
|
||||||
|
this._saveToPermissionStore();
|
||||||
|
|
||||||
|
this._onAuthDone(this._accuracyLevel);
|
||||||
|
},
|
||||||
|
|
||||||
|
_saveToPermissionStore: function() {
|
||||||
|
if (this._permStoreProxy == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this._accuracyLevel != GeoclueAccuracyLevel.NONE) {
|
||||||
|
let levelStr = accuracyLevelToString(this._accuracyLevel);
|
||||||
|
let dateStr = Math.round(Date.now() / 1000).toString();
|
||||||
|
this._permissions[this.desktopId] = [levelStr,
|
||||||
|
this._timesAllowed.toString(),
|
||||||
|
dateStr];
|
||||||
|
} else {
|
||||||
|
delete this._permissions[this.desktopId];
|
||||||
|
}
|
||||||
|
let data = GLib.Variant.new('av', {});
|
||||||
|
|
||||||
|
this._permStoreProxy.SetRemote(APP_PERMISSIONS_TABLE,
|
||||||
|
true,
|
||||||
|
APP_PERMISSIONS_ID,
|
||||||
|
this._permissions,
|
||||||
|
data,
|
||||||
|
function (result, error) {
|
||||||
|
if (error != null)
|
||||||
|
log(error.message);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const GeolocationDialog = new Lang.Class({
|
const GeolocationDialog = new Lang.Class({
|
||||||
Name: 'GeolocationDialog',
|
Name: 'GeolocationDialog',
|
||||||
Extends: ModalDialog.ModalDialog,
|
Extends: ModalDialog.ModalDialog,
|
||||||
|
Loading…
Reference in New Issue
Block a user