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:
parent
36162c2c23
commit
8badee7fa9
|
@ -27,6 +27,7 @@
|
||||||
#include <glib/gi18n-lib.h>
|
#include <glib/gi18n-lib.h>
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
|
#include <polkit/polkit.h>
|
||||||
|
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "user-controls.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,
|
static void user_manager_notify_is_loaded_cb (GObject *obj,
|
||||||
GParamSpec *pspec,
|
GParamSpec *pspec,
|
||||||
gpointer user_data);
|
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;
|
GtkApplication parent_instance;
|
||||||
|
|
||||||
|
GCancellable *cancellable; /* (owned) */
|
||||||
|
|
||||||
ActUserManager *user_manager; /* (owned) */
|
ActUserManager *user_manager; /* (owned) */
|
||||||
|
|
||||||
|
GPermission *permission; /* (owned) */
|
||||||
|
GError *permission_error; /* (nullable) (owned) */
|
||||||
|
|
||||||
MctUserSelector *user_selector;
|
MctUserSelector *user_selector;
|
||||||
MctUserControls *user_controls;
|
MctUserControls *user_controls;
|
||||||
GtkStack *main_stack;
|
GtkStack *main_stack;
|
||||||
GtkLabel *error_title;
|
GtkLabel *error_title;
|
||||||
GtkLabel *error_message;
|
GtkLabel *error_message;
|
||||||
|
GtkLockButton *lock_button;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE (MctApplication, mct_application, GTK_TYPE_APPLICATION)
|
G_DEFINE_TYPE (MctApplication, mct_application, GTK_TYPE_APPLICATION)
|
||||||
|
@ -67,7 +80,7 @@ G_DEFINE_TYPE (MctApplication, mct_application, GTK_TYPE_APPLICATION)
|
||||||
static void
|
static void
|
||||||
mct_application_init (MctApplication *self)
|
mct_application_init (MctApplication *self)
|
||||||
{
|
{
|
||||||
/* Nothing to do here. */
|
self->cancellable = g_cancellable_new ();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -93,6 +106,8 @@ mct_application_dispose (GObject *object)
|
||||||
{
|
{
|
||||||
MctApplication *self = MCT_APPLICATION (object);
|
MctApplication *self = MCT_APPLICATION (object);
|
||||||
|
|
||||||
|
g_cancellable_cancel (self->cancellable);
|
||||||
|
|
||||||
if (self->user_manager != NULL)
|
if (self->user_manager != NULL)
|
||||||
{
|
{
|
||||||
g_signal_handlers_disconnect_by_func (self->user_manager,
|
g_signal_handlers_disconnect_by_func (self->user_manager,
|
||||||
|
@ -100,6 +115,16 @@ mct_application_dispose (GObject *object)
|
||||||
g_clear_object (&self->user_manager);
|
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);
|
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_CONTROLS);
|
||||||
g_type_ensure (MCT_TYPE_USER_SELECTOR);
|
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 ();
|
builder = gtk_builder_new ();
|
||||||
|
|
||||||
g_assert (self->user_manager == NULL);
|
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->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_title = GTK_LABEL (gtk_builder_get_object (builder, "error_title"));
|
||||||
self->error_message = GTK_LABEL (gtk_builder_get_object (builder, "error_message"));
|
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. */
|
/* Connect signals. */
|
||||||
g_signal_connect_object (self->user_selector, "notify::user",
|
g_signal_connect_object (self->user_selector, "notify::user",
|
||||||
|
@ -181,16 +212,19 @@ mct_application_class_init (MctApplicationClass *klass)
|
||||||
static void
|
static void
|
||||||
update_main_stack (MctApplication *self)
|
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;
|
const gchar *new_page_name, *old_page_name;
|
||||||
GtkWidget *new_focus_widget;
|
GtkWidget *new_focus_widget;
|
||||||
|
|
||||||
/* The implementation of #ActUserManager guarantees that once is-loaded is
|
/* The implementation of #ActUserManager guarantees that once is-loaded is
|
||||||
* true, it is never reset to false. */
|
* true, it is never reset to false. */
|
||||||
g_object_get (self->user_manager, "is-loaded", &is_user_manager_loaded, NULL);
|
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. */
|
/* Handle any loading errors (including those from getting the permission). */
|
||||||
if (is_user_manager_loaded && act_user_manager_no_service (self->user_manager))
|
if ((is_user_manager_loaded && act_user_manager_no_service (self->user_manager)) ||
|
||||||
|
self->permission_error != NULL)
|
||||||
{
|
{
|
||||||
gtk_label_set_label (self->error_title,
|
gtk_label_set_label (self->error_title,
|
||||||
_("Failed to load user data from the system"));
|
_("Failed to load user data from the system"));
|
||||||
|
@ -200,7 +234,15 @@ update_main_stack (MctApplication *self)
|
||||||
new_page_name = "error";
|
new_page_name = "error";
|
||||||
new_focus_widget = NULL;
|
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_page_name = "controls";
|
||||||
new_focus_widget = GTK_WIDGET (self->user_selector);
|
new_focus_widget = GTK_WIDGET (self->user_selector);
|
||||||
|
@ -242,6 +284,45 @@ user_manager_notify_is_loaded_cb (GObject *obj,
|
||||||
update_main_stack (self);
|
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:
|
* mct_application_new:
|
||||||
*
|
*
|
||||||
|
|
|
@ -48,6 +48,61 @@
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="vexpand">True</property>
|
||||||
|
<child type="center">
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
<property name="margin">18</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Permission Required</property>
|
||||||
|
<attributes>
|
||||||
|
<attribute name="scale" value="1.4"/>
|
||||||
|
</attributes>
|
||||||
|
<child internal-child="accessible">
|
||||||
|
<object class="AtkObject">
|
||||||
|
<property name="AtkObject::accessible-role">static</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="label" translatable="yes">Permission is required to view and change parental controls settings for other users.</property>
|
||||||
|
<property name="wrap">True</property>
|
||||||
|
<child internal-child="accessible">
|
||||||
|
<object class="AtkObject">
|
||||||
|
<property name="AtkObject::accessible-role">static</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLockButton" id="lock_button">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="halign">center</property>
|
||||||
|
<property name="can-default">True</property>
|
||||||
|
<property name="has-default">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="name">unlock</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
@ -37,6 +37,7 @@ malcontent_control = executable('malcontent-control',
|
||||||
dependency('gobject-2.0', version: '>= 2.54'),
|
dependency('gobject-2.0', version: '>= 2.54'),
|
||||||
dependency('gtk+-3.0'),
|
dependency('gtk+-3.0'),
|
||||||
dependency('flatpak'),
|
dependency('flatpak'),
|
||||||
|
dependency('polkit-gobject-1'),
|
||||||
libmalcontent_dep,
|
libmalcontent_dep,
|
||||||
],
|
],
|
||||||
include_directories: root_inc,
|
include_directories: root_inc,
|
||||||
|
@ -104,6 +105,24 @@ if xmllint.found()
|
||||||
)
|
)
|
||||||
endif
|
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
|
# FIXME: Add icons and tests
|
||||||
#subdir('icons')
|
#subdir('icons')
|
||||||
#subdir('tests')
|
#subdir('tests')
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD polkit Policy Configuration 1.0//EN" "http://www.freedesktop.org/software/polkit/policyconfig-1.dtd">
|
||||||
|
<!-- Copyright © 2020 Endless Mobile, Inc. -->
|
||||||
|
<policyconfig>
|
||||||
|
<vendor>The Malcontent Project</vendor>
|
||||||
|
<vendor_url>https://gitlab.freedesktop.org/pwithnall/malcontent</vendor_url>
|
||||||
|
|
||||||
|
<action id="org.freedesktop.MalcontentControl.administration">
|
||||||
|
<description>Manage parental controls</description>
|
||||||
|
<message>Authentication is required to read and change user parental controls</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>no</allow_any>
|
||||||
|
<allow_inactive>no</allow_inactive>
|
||||||
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
|
</defaults>
|
||||||
|
<annotate key="org.freedesktop.policykit.imply">com.endlessm.ParentalControls.AppFilter.ReadAny com.endlessm.ParentalControls.AppFilter.ChangeAny</annotate>
|
||||||
|
</action>
|
||||||
|
</policyconfig>
|
|
@ -7,6 +7,7 @@ malcontent-control/gs-content-rating.c
|
||||||
malcontent-control/main.ui
|
malcontent-control/main.ui
|
||||||
malcontent-control/org.freedesktop.MalcontentControl.appdata.xml.in
|
malcontent-control/org.freedesktop.MalcontentControl.appdata.xml.in
|
||||||
malcontent-control/org.freedesktop.MalcontentControl.desktop.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.c
|
||||||
malcontent-control/restrict-applications-dialog.ui
|
malcontent-control/restrict-applications-dialog.ui
|
||||||
malcontent-control/restrict-applications-selector.c
|
malcontent-control/restrict-applications-selector.c
|
||||||
|
|
Loading…
Reference in New Issue