malcontent-control: Add polkit policy support

Add an unlock screen to the application, which is shown on startup if
the current user doesn’t have permission to view the parental controls
of other users. It requests permission using a new polkit action which
implies the various accounts-service actions we need.

This adds a dependency on `polkit-gobject-1`.

Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
Philip Withnall 2020-01-28 14:30:26 +00:00
parent 36162c2c23
commit 8badee7fa9
5 changed files with 179 additions and 5 deletions

View file

@ -27,6 +27,7 @@
#include <glib/gi18n-lib.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include <polkit/polkit.h>
#include "application.h"
#include "user-controls.h"
@ -39,6 +40,12 @@ static void user_selector_notify_user_cb (GObject *obj,
static void user_manager_notify_is_loaded_cb (GObject *obj,
GParamSpec *pspec,
gpointer user_data);
static void permission_new_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data);
static void permission_notify_allowed_cb (GObject *obj,
GParamSpec *pspec,
gpointer user_data);
/**
@ -53,13 +60,19 @@ struct _MctApplication
{
GtkApplication parent_instance;
GCancellable *cancellable; /* (owned) */
ActUserManager *user_manager; /* (owned) */
GPermission *permission; /* (owned) */
GError *permission_error; /* (nullable) (owned) */
MctUserSelector *user_selector;
MctUserControls *user_controls;
GtkStack *main_stack;
GtkLabel *error_title;
GtkLabel *error_message;
GtkLockButton *lock_button;
};
G_DEFINE_TYPE (MctApplication, mct_application, GTK_TYPE_APPLICATION)
@ -67,7 +80,7 @@ G_DEFINE_TYPE (MctApplication, mct_application, GTK_TYPE_APPLICATION)
static void
mct_application_init (MctApplication *self)
{
/* Nothing to do here. */
self->cancellable = g_cancellable_new ();
}
static void
@ -93,6 +106,8 @@ mct_application_dispose (GObject *object)
{
MctApplication *self = MCT_APPLICATION (object);
g_cancellable_cancel (self->cancellable);
if (self->user_manager != NULL)
{
g_signal_handlers_disconnect_by_func (self->user_manager,
@ -100,6 +115,16 @@ mct_application_dispose (GObject *object)
g_clear_object (&self->user_manager);
}
if (self->permission != NULL)
{
g_signal_handlers_disconnect_by_func (self->permission,
permission_notify_allowed_cb, self);
g_clear_object (&self->permission);
}
g_clear_error (&self->permission_error);
g_clear_object (&self->cancellable);
G_OBJECT_CLASS (mct_application_parent_class)->dispose (object);
}
@ -126,6 +151,11 @@ mct_application_activate (GApplication *application)
g_type_ensure (MCT_TYPE_USER_CONTROLS);
g_type_ensure (MCT_TYPE_USER_SELECTOR);
/* Start loading the permission */
polkit_permission_new ("org.freedesktop.MalcontentControl.administration",
NULL, self->cancellable,
permission_new_cb, self);
builder = gtk_builder_new ();
g_assert (self->user_manager == NULL);
@ -146,6 +176,7 @@ mct_application_activate (GApplication *application)
self->user_controls = MCT_USER_CONTROLS (gtk_builder_get_object (builder, "user_controls"));
self->error_title = GTK_LABEL (gtk_builder_get_object (builder, "error_title"));
self->error_message = GTK_LABEL (gtk_builder_get_object (builder, "error_message"));
self->lock_button = GTK_LOCK_BUTTON (gtk_builder_get_object (builder, "lock_button"));
/* Connect signals. */
g_signal_connect_object (self->user_selector, "notify::user",
@ -181,16 +212,19 @@ mct_application_class_init (MctApplicationClass *klass)
static void
update_main_stack (MctApplication *self)
{
gboolean is_user_manager_loaded;
gboolean is_user_manager_loaded, is_permission_loaded, has_permission;
const gchar *new_page_name, *old_page_name;
GtkWidget *new_focus_widget;
/* The implementation of #ActUserManager guarantees that once is-loaded is
* true, it is never reset to false. */
g_object_get (self->user_manager, "is-loaded", &is_user_manager_loaded, NULL);
is_permission_loaded = (self->permission != NULL || self->permission_error != NULL);
has_permission = (self->permission != NULL && g_permission_get_allowed (self->permission));
/* Handle any loading errors. */
if (is_user_manager_loaded && act_user_manager_no_service (self->user_manager))
/* Handle any loading errors (including those from getting the permission). */
if ((is_user_manager_loaded && act_user_manager_no_service (self->user_manager)) ||
self->permission_error != NULL)
{
gtk_label_set_label (self->error_title,
_("Failed to load user data from the system"));
@ -200,7 +234,15 @@ update_main_stack (MctApplication *self)
new_page_name = "error";
new_focus_widget = NULL;
}
else if (is_user_manager_loaded)
else if (is_permission_loaded && !has_permission)
{
gtk_lock_button_set_permission (self->lock_button, self->permission);
mct_user_controls_set_permission (self->user_controls, self->permission);
new_page_name = "unlock";
new_focus_widget = GTK_WIDGET (self->lock_button);
}
else if (is_permission_loaded && is_user_manager_loaded)
{
new_page_name = "controls";
new_focus_widget = GTK_WIDGET (self->user_selector);
@ -242,6 +284,45 @@ user_manager_notify_is_loaded_cb (GObject *obj,
update_main_stack (self);
}
static void
permission_new_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
MctApplication *self = MCT_APPLICATION (user_data);
g_autoptr(GPermission) permission = NULL;
g_autoptr(GError) local_error = NULL;
permission = polkit_permission_new_finish (result, &local_error);
if (permission == NULL)
{
g_assert (self->permission_error == NULL);
self->permission_error = g_steal_pointer (&local_error);
g_debug ("Error getting permission: %s", self->permission_error->message);
}
else
{
g_assert (self->permission == NULL);
self->permission = g_steal_pointer (&permission);
g_signal_connect (self->permission, "notify::allowed",
G_CALLBACK (permission_notify_allowed_cb), self);
}
/* Recalculate the UI. */
update_main_stack (self);
}
static void
permission_notify_allowed_cb (GObject *obj,
GParamSpec *pspec,
gpointer user_data)
{
MctApplication *self = MCT_APPLICATION (user_data);
update_main_stack (self);
}
/**
* mct_application_new:
*