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