[gnome-software/wip/rancell/permissions] Add a basic permissions system
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-software/wip/rancell/permissions] Add a basic permissions system
- Date: Fri, 26 May 2017 04:31:28 +0000 (UTC)
commit ef9a65f0c1be4ca00ec9e7d7efc873205edfa702
Author: Robert Ancell <robert ancell canonical com>
Date: Fri May 26 16:30:56 2017 +1200
Add a basic permissions system
lib/gs-app.c | 47 +++++++++++
lib/gs-app.h | 5 +
lib/gs-permission.c | 178 +++++++++++++++++++++++++++++++++++++++++
lib/gs-permission.h | 53 ++++++++++++
lib/meson.build | 2 +
plugins/snap/gs-plugin-snap.c | 27 ++++++
src/gs-details-page.c | 40 +++++++++
src/gs-details-page.ui | 55 ++++++++++++-
src/gtk-style-hc.css | 5 +
src/gtk-style.css | 5 +
10 files changed, 412 insertions(+), 5 deletions(-)
---
diff --git a/lib/gs-app.c b/lib/gs-app.c
index aee3432..ad12ae9 100644
--- a/lib/gs-app.c
+++ b/lib/gs-app.c
@@ -87,6 +87,7 @@ struct _GsApp
gchar **menu_path;
gchar *origin;
gchar *origin_hostname;
+ GPtrArray *permissions;
gchar *update_version;
gchar *update_version_ui;
gchar *update_details;
@@ -422,6 +423,16 @@ gs_app_to_string (GsApp *app)
gs_app_kv_lpad (str, "icon-filename",
as_icon_get_filename (icon));
}
+ for (i = 0; i < app->permissions->len; i++) {
+ GsPermission *permission;
+ g_autofree gchar *key = NULL;
+
+ permission = g_ptr_array_index (app->permissions, i);
+ key = g_strdup_printf ("permission-%02u", i);
+ gs_app_kv_printf (str, key, "[%s] %s",
+ gs_permission_get_label (permission),
+ gs_permission_get_enabled (permission) ? "true" : "false");
+ }
if (app->match_value != 0)
gs_app_kv_printf (str, "match-value", "%05x", app->match_value);
if (app->priority != 0)
@@ -2215,6 +2226,40 @@ gs_app_set_origin_hostname (GsApp *app, const gchar *origin_hostname)
}
/**
+ * gs_app_add_permission:
+ * @app: a #GsApp
+ * @permission: a #GsPermission
+ *
+ * Adds a permission to the applicaton.
+ *
+ * Since: 3.26
+ **/
+void
+gs_app_add_permission (GsApp *app, GsPermission *permission)
+{
+ g_return_if_fail (GS_IS_APP (app));
+ g_return_if_fail (GS_IS_PERMISSION (permission));
+ g_ptr_array_add (app->permissions, g_object_ref (permission));
+}
+
+/**
+ * gs_app_get_permissions:
+ * @app: a #GsApp
+ *
+ * Gets the list of permissions.
+ *
+ * Returns: (element-type GsPermission) (transfer none): a list
+ *
+ * Since: 3.26
+ **/
+GPtrArray *
+gs_app_get_permissions (GsApp *app)
+{
+ g_return_val_if_fail (GS_IS_APP (app), NULL);
+ return app->permissions;
+}
+
+/**
* gs_app_add_screenshot:
* @app: a #GsApp
* @screenshot: a #AsScreenshot
@@ -3592,6 +3637,7 @@ gs_app_dispose (GObject *object)
g_clear_pointer (&app->reviews, g_ptr_array_unref);
g_clear_pointer (&app->provides, g_ptr_array_unref);
g_clear_pointer (&app->icons, g_ptr_array_unref);
+ g_clear_pointer (&app->permissions, g_ptr_array_unref);
G_OBJECT_CLASS (gs_app_parent_class)->dispose (object);
}
@@ -3758,6 +3804,7 @@ gs_app_init (GsApp *app)
app->reviews = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->provides = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->icons = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
+ app->permissions = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
app->metadata = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
diff --git a/lib/gs-app.h b/lib/gs-app.h
index bddc068..de7fb36 100644
--- a/lib/gs-app.h
+++ b/lib/gs-app.h
@@ -27,6 +27,8 @@
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <appstream-glib.h>
+#include "gs-permission.h"
+
G_BEGIN_DECLS
#define GS_TYPE_APP (gs_app_get_type ())
@@ -181,6 +183,9 @@ void gs_app_set_origin (GsApp *app,
const gchar *gs_app_get_origin_hostname (GsApp *app);
void gs_app_set_origin_hostname (GsApp *app,
const gchar *origin_hostname);
+GPtrArray *gs_app_get_permissions (GsApp *app);
+void gs_app_add_permission (GsApp *app,
+ GsPermission *permission);
GPtrArray *gs_app_get_screenshots (GsApp *app);
void gs_app_add_screenshot (GsApp *app,
AsScreenshot *screenshot);
diff --git a/lib/gs-permission.c b/lib/gs-permission.c
new file mode 100644
index 0000000..f4c19ac
--- /dev/null
+++ b/lib/gs-permission.c
@@ -0,0 +1,178 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gs-permission.h"
+
+struct _GsPermission
+{
+ GObject parent_instance;
+
+ gchar *label;
+ gboolean enabled;
+ GHashTable *metadata; /* utf8: utf8 */
+};
+
+G_DEFINE_TYPE (GsPermission, gs_permission, G_TYPE_OBJECT)
+
+/**
+ * gs_permission_get_label:
+ * @permission: a #GsPermission
+ *
+ * Get the label for this permission.
+ *
+ * Returns: a label string.
+ */
+const gchar *
+gs_permission_get_label (GsPermission *permission)
+{
+ g_return_val_if_fail (GS_IS_PERMISSION (permission), NULL);
+ return permission->label;
+}
+
+/**
+ * gs_permission_set_label:
+ * @permission: a #GsPermission
+ * @label: a label string.
+ *
+ * Set the label for this permission.
+ */
+void
+gs_permission_set_label (GsPermission *permission, const gchar *label)
+{
+ g_return_if_fail (GS_IS_PERMISSION (permission));
+ g_free (permission->label);
+ permission->label = g_strdup (label);
+}
+
+/**
+ * gs_permission_get_metadata_item:
+ * @auth: a #GsPermission
+ * @key: a string
+ *
+ * Gets some metadata from a permission object.
+ * It is left for the the plugin to use this method as required, but a
+ * typical use would be to retrieve an ID for this permission.
+ *
+ * Returns: A string value, or %NULL for not found
+ */
+const gchar *
+gs_permission_get_metadata_item (GsPermission *auth, const gchar *key)
+{
+ g_return_val_if_fail (GS_IS_PERMISSION (auth), NULL);
+ g_return_val_if_fail (key != NULL, NULL);
+ return g_hash_table_lookup (auth->metadata, key);
+}
+
+/**
+ * gs_permission_add_metadata:
+ * @auth: a #GsPermission
+ * @key: a string
+ * @value: a string
+ *
+ * Adds metadata to the permission object.
+ * It is left for the the plugin to use this method as required, but a
+ * typical use would be to store an ID for this permission.
+ */
+void
+gs_permission_add_metadata (GsPermission *auth, const gchar *key, const gchar *value)
+{
+ g_return_if_fail (GS_IS_PERMISSION (auth));
+ g_hash_table_insert (auth->metadata, g_strdup (key), g_strdup (value));
+}
+
+/**
+ * gs_permission_get_enabled:
+ * @permission: a #GsPermission
+ *
+ * Get if this permission is enabled.
+ *
+ * Returns: %TRUE if enabled
+ */
+gboolean
+gs_permission_get_enabled (GsPermission *permission)
+{
+ g_return_val_if_fail (GS_IS_PERMISSION (permission), 0);
+ return permission->enabled;
+}
+
+/**
+ * gs_permission_set_enabled:
+ * @permission: a #GsPermission
+ * @enabled: %TRUE if this permission is enabled.
+ *
+ * Set if this permission is enabled.
+ */
+void
+gs_permission_set_enabled (GsPermission *permission, gboolean enabled)
+{
+ g_return_if_fail (GS_IS_PERMISSION (permission));
+ permission->enabled = enabled;
+}
+
+static void
+gs_permission_finalize (GObject *object)
+{
+ GsPermission *permission = GS_PERMISSION (object);
+
+ g_free (permission->label);
+ g_hash_table_unref (permission->metadata);
+
+ G_OBJECT_CLASS (gs_permission_parent_class)->finalize (object);
+}
+
+static void
+gs_permission_class_init (GsPermissionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gs_permission_finalize;
+}
+
+static void
+gs_permission_init (GsPermission *permission)
+{
+ permission->metadata = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, g_free);
+}
+
+/**
+ * gs_permission_new:
+ * @label: An ISO 4217 label code, e.g. "USD"
+ * @enabled: %TRUE if this permission is enabled.
+ *
+ * Creates a new permission object.
+ *
+ * Return value: a new #GsPermission object.
+ **/
+GsPermission *
+gs_permission_new (const gchar *label, gboolean enabled)
+{
+ GsPermission *permission;
+ permission = g_object_new (GS_TYPE_PERMISSION, NULL);
+ permission->label = g_strdup (label);
+ permission->enabled = enabled;
+ return GS_PERMISSION (permission);
+}
+
+/* vim: set noexpandtab: */
diff --git a/lib/gs-permission.h b/lib/gs-permission.h
new file mode 100644
index 0000000..a54c022
--- /dev/null
+++ b/lib/gs-permission.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2017 Canonical Ltd.
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GS_PERMISSION_H
+#define __GS_PERMISSION_H
+
+#include <glib-object.h>
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_PERMISSION (gs_permission_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsPermission, gs_permission, GS, PERMISSION, GObject)
+
+GsPermission *gs_permission_new (const gchar *label,
+ gboolean enabled);
+
+const gchar *gs_permission_get_label (GsPermission *permission);
+void gs_permission_set_label (GsPermission *permission,
+ const gchar *label);
+const gchar *gs_permission_get_metadata_item (GsPermission *permission,
+ const gchar *key);
+void gs_permission_add_metadata (GsPermission *permission,
+ const gchar *key,
+ const gchar *value);
+gboolean gs_permission_get_enabled (GsPermission *permission);
+void gs_permission_set_enabled (GsPermission *permission,
+ gboolean enabled);
+
+G_END_DECLS
+
+#endif /* __GS_PERMISSION_H */
+
+/* vim: set noexpandtab: */
diff --git a/lib/meson.build b/lib/meson.build
index 95a5852..946992c 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -43,6 +43,7 @@ install_headers([
'gs-auth.h',
'gs-category.h',
'gs-os-release.h',
+ 'gs-permission.h',
'gs-plugin.h',
'gs-plugin-event.h',
'gs-plugin-types.h',
@@ -61,6 +62,7 @@ libgnomesoftware = static_library(
'gs-category.c',
'gs-debug.c',
'gs-os-release.c',
+ 'gs-permission.c',
'gs-plugin.c',
'gs-plugin-event.c',
'gs-plugin-job.c',
diff --git a/plugins/snap/gs-plugin-snap.c b/plugins/snap/gs-plugin-snap.c
index 0f55a26..00d609c 100644
--- a/plugins/snap/gs-plugin-snap.c
+++ b/plugins/snap/gs-plugin-snap.c
@@ -420,6 +420,9 @@ gs_plugin_refine_app (GsPlugin *plugin,
g_autoptr(SnapdClient) client = NULL;
const gchar *id;
g_autoptr(SnapdSnap) snap = NULL;
+ g_autoptr(GPtrArray) plugs = NULL;
+ g_autoptr(GPtrArray) slots = NULL;
+ guint i;
/* not us */
if (g_strcmp0 (gs_app_get_management_plugin (app), "snap") != 0)
@@ -437,6 +440,30 @@ gs_plugin_refine_app (GsPlugin *plugin,
if (!gs_plugin_snap_refine_app (plugin, app, snap, FALSE, cancellable, error))
return FALSE;
+ if (gs_app_get_permissions (app)->len == 0) {
+ if (!snapd_client_get_interfaces_sync (client, &plugs, &slots, cancellable, error))
+ return FALSE;
+ for (i = 0; i < plugs->len; i++) {
+ SnapdPlug *plug = plugs->pdata[i];
+ const gchar *name;
+ g_autoptr(GsPermission) permission = NULL;
+
+ /* skip if not relating to this snap */
+ if (g_strcmp0 (snapd_plug_get_snap (plug), snapd_snap_get_name (snap)) != 0)
+ continue;
+
+ /* map interfaces to known permissions */
+ name = snapd_plug_get_name (plug);
+ if (g_strcmp0 (name, "camera") == 0)
+ ;
+ else
+ continue;
+
+ permission = gs_permission_new (name, snapd_plug_get_connections (plug)->len > 0);
+ gs_app_add_permission (app, permission);
+ }
+ }
+
return TRUE;
}
diff --git a/src/gs-details-page.c b/src/gs-details-page.c
index 6879105..6a06421 100644
--- a/src/gs-details-page.c
+++ b/src/gs-details-page.c
@@ -76,6 +76,7 @@ struct _GsDetailsPage
GtkWidget *box_details_screenshot_main;
GtkWidget *box_details_screenshot_thumbnails;
GtkWidget *box_details_license_list;
+ GtkWidget *box_permissions;
GtkWidget *button_details_launch;
GtkWidget *button_details_add_shortcut;
GtkWidget *button_details_remove_shortcut;
@@ -121,6 +122,7 @@ struct _GsDetailsPage
GtkWidget *spinner_remove;
GtkWidget *stack_details;
GtkWidget *grid_details_kudo;
+ GtkWidget *grid_permissions;
GtkWidget *image_details_kudo_docs;
GtkWidget *image_details_kudo_sandboxed;
GtkWidget *image_details_kudo_integration;
@@ -507,6 +509,40 @@ gs_details_page_notify_state_changed_cb (GsApp *app,
}
static void
+gs_details_page_refresh_permissions (GsDetailsPage *self)
+{
+ GPtrArray *permissions;
+ guint i;
+
+ /* nothing to show */
+ if (self->app == NULL)
+ return;
+
+ /* show or hide the entire permissions section */
+ permissions = gs_app_get_permissions (self->app);
+ gtk_widget_set_visible (self->box_permissions, permissions->len > 0);
+
+ gtk_container_foreach (GTK_CONTAINER (self->grid_permissions),
+ (GtkCallback) gtk_widget_destroy, NULL);
+ for (i = 0; i < permissions->len; i++) {
+ GsPermission *permission = g_ptr_array_index (permissions, i);
+ GtkWidget *sw, *label;
+
+ sw = gtk_switch_new ();
+ gtk_widget_set_visible (sw, TRUE);
+ gtk_switch_set_active (GTK_SWITCH (sw), gs_permission_get_enabled (permission));
+ gtk_grid_attach (GTK_GRID (self->grid_permissions), sw, 0, i, 1, 1);
+
+ label = gtk_label_new (gs_permission_get_label (permission));
+ g_object_set (label,
+ "xalign", 0.0,
+ NULL);
+ gtk_widget_set_visible (label, TRUE);
+ gtk_grid_attach (GTK_GRID (self->grid_permissions), label, 1, i, 1, 1);
+ }
+}
+
+static void
gs_details_page_screenshot_selected_cb (GtkListBox *list,
GtkListBoxRow *row,
GsDetailsPage *self)
@@ -1444,6 +1480,7 @@ gs_details_page_app_refine_cb (GObject *source,
app_dump = gs_app_to_string (self->app);
g_debug ("%s", app_dump);
+ gs_details_page_refresh_permissions (self);
gs_details_page_refresh_screenshots (self);
gs_details_page_refresh_addons (self);
gs_details_page_refresh_reviews (self);
@@ -1493,6 +1530,7 @@ set_app (GsDetailsPage *self, GsApp *app)
/* change widgets */
gs_page_switch_to (GS_PAGE (self), TRUE);
+ gs_details_page_refresh_permissions (self);
gs_details_page_refresh_screenshots (self);
gs_details_page_refresh_addons (self);
gs_details_page_refresh_reviews (self);
@@ -2228,6 +2266,7 @@ gs_details_page_class_init (GsDetailsPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, box_details_screenshot_main);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, box_details_screenshot_thumbnails);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, box_details_license_list);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, box_permissions);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, button_details_launch);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, button_details_add_shortcut);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, button_details_remove_shortcut);
@@ -2271,6 +2310,7 @@ gs_details_page_class_init (GsDetailsPageClass *klass)
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, spinner_remove);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, stack_details);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, grid_details_kudo);
+ gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, grid_permissions);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, image_details_kudo_docs);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, image_details_kudo_sandboxed);
gtk_widget_class_bind_template_child (widget_class, GsDetailsPage, image_details_kudo_integration);
diff --git a/src/gs-details-page.ui b/src/gs-details-page.ui
index cacd1a0..888ca7f 100644
--- a/src/gs-details-page.ui
+++ b/src/gs-details-page.ui
@@ -571,7 +571,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
- <property name="position">0</property>
+ <property name="position">1</property>
</packing>
</child>
</object>
@@ -582,6 +582,51 @@
</packing>
</child>
<child>
+ <object class="GtkBox" id="box_permissions">
+ <property name="visible">False</property>
+ <property name="orientation">vertical</property>
+ <property name="margin_bottom">26</property>
+ <property name="spacing">30</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="valign">start</property>
+ <property name="hexpand">True</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Permissions</property>
+ <style>
+ <class name="application-permissions-title"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid_permissions">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">9</property>
+ <property name="column_spacing">24</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">11</property>
+ </packing>
+ </child>
+ <child>
<object class="GtkLabel" id="application_details_details_title">
<property name="visible">True</property>
<property name="can_focus">False</property>
@@ -597,7 +642,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">11</property>
+ <property name="position">12</property>
</packing>
</child>
<child>
@@ -1135,7 +1180,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">12</property>
+ <property name="position">13</property>
</packing>
</child>
<child>
@@ -1198,7 +1243,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">13</property>
+ <property name="position">14</property>
</packing>
</child>
<child>
@@ -1263,7 +1308,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">14</property>
+ <property name="position">15</property>
</packing>
</child>
</object>
diff --git a/src/gtk-style-hc.css b/src/gtk-style-hc.css
index 5dd475c..fb8cfdc 100644
--- a/src/gtk-style-hc.css
+++ b/src/gtk-style-hc.css
@@ -96,6 +96,11 @@
text-shadow: none;
}
+.application-permissions-title {
+ font-weight: bold;
+ font-size: 125%;
+}
+
.application-details-title {
font-weight: bold;
font-size: 125%;
diff --git a/src/gtk-style.css b/src/gtk-style.css
index c259469..534d56f 100644
--- a/src/gtk-style.css
+++ b/src/gtk-style.css
@@ -219,6 +219,11 @@
text-shadow: none;
}
+.application-permissions-title {
+ font-weight: bold;
+ font-size: 125%;
+}
+
.application-details-title {
font-weight: bold;
font-size: 125%;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]