From 8badee7fa9803ca124261cacaf66ef6e5f72b31d Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 28 Jan 2020 14:30:26 +0000 Subject: [PATCH] malcontent-control: Add polkit policy support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- malcontent-control/application.c | 91 ++++++++++++++++++- malcontent-control/main.ui | 55 +++++++++++ malcontent-control/meson.build | 19 ++++ ...rg.freedesktop.MalcontentControl.policy.in | 18 ++++ po/POTFILES.in | 1 + 5 files changed, 179 insertions(+), 5 deletions(-) create mode 100644 malcontent-control/org.freedesktop.MalcontentControl.policy.in diff --git a/malcontent-control/application.c b/malcontent-control/application.c index 5e30b11..8197789 100644 --- a/malcontent-control/application.c +++ b/malcontent-control/application.c @@ -27,6 +27,7 @@ #include #include #include +#include #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: * diff --git a/malcontent-control/main.ui b/malcontent-control/main.ui index 31f0d48..9c8bf9c 100644 --- a/malcontent-control/main.ui +++ b/malcontent-control/main.ui @@ -48,6 +48,61 @@ + + + True + vertical + True + True + + + True + vertical + 12 + 18 + + + True + Permission Required + + + + + + static + + + + + + + True + Permission is required to view and change parental controls settings for other users. + True + + + static + + + + + + + True + center + True + True + True + + + + + + + unlock + + + True diff --git a/malcontent-control/meson.build b/malcontent-control/meson.build index 82a73b4..528ed3c 100644 --- a/malcontent-control/meson.build +++ b/malcontent-control/meson.build @@ -37,6 +37,7 @@ malcontent_control = executable('malcontent-control', dependency('gobject-2.0', version: '>= 2.54'), dependency('gtk+-3.0'), dependency('flatpak'), + dependency('polkit-gobject-1'), libmalcontent_dep, ], include_directories: root_inc, @@ -104,6 +105,24 @@ if xmllint.found() ) endif +policy_file = i18n.merge_file('policy-file', + input: '@0@.policy.in'.format(application_id), + output: '@0@.policy'.format(application_id), + po_dir: join_paths(meson.source_root(), 'po'), + install: true, + install_dir: join_paths(get_option('datadir'), 'polkit-1', 'actions'), +) +if xmllint.found() + test( + 'validate-policy', xmllint, + args: [ + '--nonet', '--noblanks', '--noout', + policy_file, + ], + suite: ['malcontent-control'], + ) +endif + # FIXME: Add icons and tests #subdir('icons') #subdir('tests') diff --git a/malcontent-control/org.freedesktop.MalcontentControl.policy.in b/malcontent-control/org.freedesktop.MalcontentControl.policy.in new file mode 100644 index 0000000..b2a93ac --- /dev/null +++ b/malcontent-control/org.freedesktop.MalcontentControl.policy.in @@ -0,0 +1,18 @@ + + + + + The Malcontent Project + https://gitlab.freedesktop.org/pwithnall/malcontent + + + Manage parental controls + Authentication is required to read and change user parental controls + + no + no + auth_admin_keep + + com.endlessm.ParentalControls.AppFilter.ReadAny com.endlessm.ParentalControls.AppFilter.ChangeAny + + diff --git a/po/POTFILES.in b/po/POTFILES.in index aaa1ca0..d26b86e 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -7,6 +7,7 @@ malcontent-control/gs-content-rating.c malcontent-control/main.ui malcontent-control/org.freedesktop.MalcontentControl.appdata.xml.in malcontent-control/org.freedesktop.MalcontentControl.desktop.in +malcontent-control/org.freedesktop.MalcontentControl.policy.in malcontent-control/restrict-applications-dialog.c malcontent-control/restrict-applications-dialog.ui malcontent-control/restrict-applications-selector.c