[gnome-shell] location: Add AppAuthorizer class



commit 34fc4547647bceff56a0e7a853e14a2ea454c69c
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Mon Feb 15 19:45:38 2016 +0000

    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

 js/ui/status/location.js |  159 ++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 159 insertions(+), 0 deletions(-)
---
diff --git a/js/ui/status/location.js b/js/ui/status/location.js
index 2dad0b8..ff39432 100644
--- a/js/ui/status/location.js
+++ b/js/ui/status/location.js
@@ -17,6 +17,9 @@ const LOCATION_SCHEMA = 'org.gnome.system.location';
 const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
 const ENABLED = 'enabled';
 
+const APP_PERMISSIONS_TABLE = 'desktop';
+const APP_PERMISSIONS_ID = 'geolocation';
+
 const GeoclueAccuracyLevel = {
     NONE: 0,
     COUNTRY: 1,
@@ -26,6 +29,15 @@ const GeoclueAccuracyLevel = {
     EXACT: 8
 };
 
+function accuracyLevelToString(accuracyLevel) {
+    for (let key in GeoclueAccuracyLevel) {
+        if (GeoclueAccuracyLevel[key] == accuracyLevel)
+            return key;
+    }
+
+    return 'NONE';
+}
+
 var GeoclueIface = '<node> \
   <interface name="org.freedesktop.GeoClue2.Manager"> \
     <property name="InUse" type="b" access="read"/> \
@@ -50,6 +62,24 @@ var AgentIface = '<node> \
   </interface> \
 </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({
     Name: 'LocationIndicator',
     Extends: PanelMenu.SystemIndicator,
@@ -222,6 +252,135 @@ function clamp(value, min, max) {
     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({
     Name: 'GeolocationDialog',
     Extends: ModalDialog.ModalDialog,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]