libmalcontent: Factor getting/setting app filter into a manager

Create a new MctManager object which is used as the anchor for getting
or setting MctAppFilters.

This changes the API naming around quite a bit, but doesn’t really
change its behaviour or functionality — see the tests for examples of
how little things change.

This is one step on the way to emitting a signal (from MctManager) when
a user’s parental controls change.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

https://gitlab.freedesktop.org/pwithnall/malcontent/issues/1
This commit is contained in:
Philip Withnall 2019-04-24 12:44:50 +01:00
parent 388dedbff2
commit 68ebe8b568
8 changed files with 1074 additions and 829 deletions

View File

@ -0,0 +1,63 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright © 2018, 2019 Endless Mobile, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
* - Philip Withnall <withnall@endlessm.com>
*/
#pragma once
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <libmalcontent/app-filter.h>
G_BEGIN_DECLS
/**
* MctAppFilterListType:
* @MCT_APP_FILTER_LIST_BLACKLIST: Any program in the list is not allowed to
* be run.
* @MCT_APP_FILTER_LIST_WHITELIST: Any program not in the list is not allowed
* to be run.
*
* Different semantics for interpreting an application list.
*
* Since: 0.2.0
*/
typedef enum
{
MCT_APP_FILTER_LIST_BLACKLIST,
MCT_APP_FILTER_LIST_WHITELIST,
} MctAppFilterListType;
struct _MctAppFilter
{
gint ref_count;
uid_t user_id;
gchar **app_list; /* (owned) (array zero-terminated=1) */
MctAppFilterListType app_list_type;
GVariant *oars_ratings; /* (type a{ss}) (owned non-floating) */
gboolean allow_user_installation;
gboolean allow_system_installation;
};
G_END_DECLS

View File

@ -29,39 +29,12 @@
#include <gio/gio.h> #include <gio/gio.h>
#include <libmalcontent/app-filter.h> #include <libmalcontent/app-filter.h>
#include "libmalcontent/app-filter-private.h"
G_DEFINE_QUARK (MctAppFilterError, mct_app_filter_error) G_DEFINE_QUARK (MctAppFilterError, mct_app_filter_error)
/** /* struct _MctAppFilter is defined in app-filter-private.h */
* MctAppFilterListType:
* @MCT_APP_FILTER_LIST_BLACKLIST: Any program in the list is not allowed to
* be run.
* @MCT_APP_FILTER_LIST_WHITELIST: Any program not in the list is not allowed
* to be run.
*
* Different semantics for interpreting an application list.
*
* Since: 0.2.0
*/
typedef enum
{
MCT_APP_FILTER_LIST_BLACKLIST,
MCT_APP_FILTER_LIST_WHITELIST,
} MctAppFilterListType;
struct _MctAppFilter
{
gint ref_count;
uid_t user_id;
gchar **app_list; /* (owned) (array zero-terminated=1) */
MctAppFilterListType app_list_type;
GVariant *oars_ratings; /* (type a{ss}) (owned non-floating) */
gboolean allow_user_installation;
gboolean allow_system_installation;
};
G_DEFINE_BOXED_TYPE (MctAppFilter, mct_app_filter, G_DEFINE_BOXED_TYPE (MctAppFilter, mct_app_filter,
mct_app_filter_ref, mct_app_filter_unref) mct_app_filter_ref, mct_app_filter_unref)
@ -453,639 +426,6 @@ mct_app_filter_is_system_installation_allowed (MctAppFilter *filter)
return filter->allow_system_installation; return filter->allow_system_installation;
} }
/**
* _mct_app_filter_build_app_filter_variant:
* @filter: an #MctAppFilter
*
* Build a #GVariant which contains the app filter from @filter, in the format
* used for storing it in AccountsService.
*
* Returns: (transfer floating): a new, floating #GVariant containing the app
* filter
* Since: 0.2.0
*/
static GVariant *
_mct_app_filter_build_app_filter_variant (MctAppFilter *filter)
{
g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(bas)"));
g_return_val_if_fail (filter != NULL, NULL);
g_return_val_if_fail (filter->ref_count >= 1, NULL);
g_variant_builder_add (&builder, "b",
(filter->app_list_type == MCT_APP_FILTER_LIST_WHITELIST));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
for (gsize i = 0; filter->app_list[i] != NULL; i++)
g_variant_builder_add (&builder, "s", filter->app_list[i]);
g_variant_builder_close (&builder);
return g_variant_builder_end (&builder);
}
/* Check if @error is a D-Bus remote error matching @expected_error_name. */
static gboolean
bus_remote_error_matches (const GError *error,
const gchar *expected_error_name)
{
g_autofree gchar *error_name = NULL;
if (!g_dbus_error_is_remote_error (error))
return FALSE;
error_name = g_dbus_error_get_remote_error (error);
return g_str_equal (error_name, expected_error_name);
}
/* Convert a #GDBusError into a #MctAppFilter error. */
static GError *
bus_error_to_app_filter_error (const GError *bus_error,
uid_t user_id)
{
if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED) ||
bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.PermissionDenied"))
return g_error_new (MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED,
_("Not allowed to query app filter data for user %u"),
(guint) user_id);
else if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD) ||
bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.Failed"))
return g_error_new (MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_INVALID_USER,
_("User %u does not exist"), (guint) user_id);
else
return g_error_copy (bus_error);
}
/* Find the object path for the given @user_id on the accountsservice D-Bus
* interface, by calling its FindUserById() method. This is a synchronous,
* blocking function. */
static gchar *
accounts_find_user_by_id (GDBusConnection *connection,
uid_t user_id,
gboolean allow_interactive_authorization,
GCancellable *cancellable,
GError **error)
{
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) result_variant = NULL;
g_autoptr(GError) local_error = NULL;
result_variant =
g_dbus_connection_call_sync (connection,
"org.freedesktop.Accounts",
"/org/freedesktop/Accounts",
"org.freedesktop.Accounts",
"FindUserById",
g_variant_new ("(x)", (gint64) user_id),
G_VARIANT_TYPE ("(o)"),
allow_interactive_authorization
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_autoptr(GError) app_filter_error = bus_error_to_app_filter_error (local_error,
user_id);
g_propagate_error (error, g_steal_pointer (&app_filter_error));
return NULL;
}
g_variant_get (result_variant, "(o)", &object_path);
return g_steal_pointer (&object_path);
}
/**
* mct_get_app_filter:
* @connection: (nullable): a #GDBusConnection to the system bus, or %NULL to
* use the default
* @user_id: ID of the user to query, typically coming from getuid()
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronous version of mct_get_app_filter_async().
*
* Returns: (transfer full): app filter for the queried user
* Since: 0.3.0
*/
MctAppFilter *
mct_get_app_filter (GDBusConnection *connection,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GError **error)
{
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) result_variant = NULL;
g_autoptr(GVariant) properties = NULL;
g_autoptr(GError) local_error = NULL;
g_autoptr(MctAppFilter) app_filter = NULL;
gboolean is_whitelist;
g_auto(GStrv) app_list = NULL;
const gchar *content_rating_kind;
g_autoptr(GVariant) oars_variant = NULL;
gboolean allow_user_installation;
gboolean allow_system_installation;
g_return_val_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (connection == NULL)
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
if (connection == NULL)
return NULL;
object_path = accounts_find_user_by_id (connection, user_id,
(flags & MCT_GET_APP_FILTER_FLAGS_INTERACTIVE),
cancellable, error);
if (object_path == NULL)
return NULL;
result_variant =
g_dbus_connection_call_sync (connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"GetAll",
g_variant_new ("(s)", "com.endlessm.ParentalControls.AppFilter"),
G_VARIANT_TYPE ("(a{sv})"),
(flags & MCT_GET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_autoptr(GError) app_filter_error = NULL;
if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS))
{
/* o.fd.D.GetAll() will return InvalidArgs errors if
* accountsservice doesnt have the com.endlessm.ParentalControls.AppFilter
* extension interface installed. */
app_filter_error = g_error_new_literal (MCT_APP_FILTER_ERROR,
MCT_APP_FILTER_ERROR_DISABLED,
_("App filtering is globally disabled"));
}
else
{
app_filter_error = bus_error_to_app_filter_error (local_error,
user_id);
}
g_propagate_error (error, g_steal_pointer (&app_filter_error));
return NULL;
}
/* Extract the properties we care about. They may be silently omitted from the
* results if we dont have permission to access them. */
properties = g_variant_get_child_value (result_variant, 0);
if (!g_variant_lookup (properties, "AppFilter", "(b^as)",
&is_whitelist, &app_list))
{
g_set_error (error, MCT_APP_FILTER_ERROR,
MCT_APP_FILTER_ERROR_PERMISSION_DENIED,
_("Not allowed to query app filter data for user %u"),
(guint) user_id);
return NULL;
}
if (!g_variant_lookup (properties, "OarsFilter", "(&s@a{ss})",
&content_rating_kind, &oars_variant))
{
/* Default value. */
content_rating_kind = "oars-1.1";
oars_variant = g_variant_new ("a{ss}", NULL);
}
/* Check that the OARS filter is in a format we support. Currently, thats
* only oars-1.0 and oars-1.1. */
if (!g_str_equal (content_rating_kind, "oars-1.0") &&
!g_str_equal (content_rating_kind, "oars-1.1"))
{
g_set_error (error, MCT_APP_FILTER_ERROR,
MCT_APP_FILTER_ERROR_INVALID_DATA,
_("OARS filter for user %u has an unrecognized kind %s"),
(guint) user_id, content_rating_kind);
return NULL;
}
if (!g_variant_lookup (properties, "AllowUserInstallation", "b",
&allow_user_installation))
{
/* Default value. */
allow_user_installation = TRUE;
}
if (!g_variant_lookup (properties, "AllowSystemInstallation", "b",
&allow_system_installation))
{
/* Default value. */
allow_system_installation = FALSE;
}
/* Success. Create an #MctAppFilter object to contain the results. */
app_filter = g_new0 (MctAppFilter, 1);
app_filter->ref_count = 1;
app_filter->user_id = user_id;
app_filter->app_list = g_steal_pointer (&app_list);
app_filter->app_list_type =
is_whitelist ? MCT_APP_FILTER_LIST_WHITELIST : MCT_APP_FILTER_LIST_BLACKLIST;
app_filter->oars_ratings = g_steal_pointer (&oars_variant);
app_filter->allow_user_installation = allow_user_installation;
app_filter->allow_system_installation = allow_system_installation;
return g_steal_pointer (&app_filter);
}
static void get_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable);
typedef struct
{
GDBusConnection *connection; /* (nullable) (owned) */
uid_t user_id;
MctGetAppFilterFlags flags;
} GetAppFilterData;
static void
get_app_filter_data_free (GetAppFilterData *data)
{
g_clear_object (&data->connection);
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetAppFilterData, get_app_filter_data_free)
/**
* mct_get_app_filter_async:
* @connection: (nullable): a #GDBusConnection to the system bus, or %NULL to
* use the default
* @user_id: ID of the user to query, typically coming from getuid()
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: a #GAsyncReadyCallback
* @user_data: user data to pass to @callback
*
* Asynchronously get a snapshot of the app filter settings for the given
* @user_id.
*
* @connection should be a connection to the system bus, where accounts-service
* runs. Its provided mostly for testing purposes, or to allow an existing
* connection to be re-used. Pass %NULL to use the default connection.
*
* On failure, an #MctAppFilterError, a #GDBusError or a #GIOError will be
* returned.
*
* Since: 0.3.0
*/
void
mct_get_app_filter_async (GDBusConnection *connection,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GetAppFilterData) data = NULL;
g_return_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, mct_get_app_filter_async);
data = g_new0 (GetAppFilterData, 1);
data->connection = (connection != NULL) ? g_object_ref (connection) : NULL;
data->user_id = user_id;
data->flags = flags;
g_task_set_task_data (task, g_steal_pointer (&data),
(GDestroyNotify) get_app_filter_data_free);
g_task_run_in_thread (task, get_app_filter_thread_cb);
}
static void
get_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr(MctAppFilter) filter = NULL;
GetAppFilterData *data = task_data;
g_autoptr(GError) local_error = NULL;
filter = mct_get_app_filter (data->connection, data->user_id,
data->flags,
cancellable, &local_error);
if (local_error != NULL)
g_task_return_error (task, g_steal_pointer (&local_error));
else
g_task_return_pointer (task, g_steal_pointer (&filter),
(GDestroyNotify) mct_app_filter_unref);
}
/**
* mct_get_app_filter_finish:
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finish an asynchronous operation to get the app filter for a user, started
* with mct_get_app_filter_async().
*
* Returns: (transfer full): app filter for the queried user
* Since: 0.2.0
*/
MctAppFilter *
mct_get_app_filter_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* mct_set_app_filter:
* @connection: (nullable): a #GDBusConnection to the system bus, or %NULL to
* use the default
* @user_id: ID of the user to set the filter for, typically coming from getuid()
* @app_filter: (transfer none): the app filter to set for the user
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronous version of mct_set_app_filter_async().
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: 0.3.0
*/
gboolean
mct_set_app_filter (GDBusConnection *connection,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GError **error)
{
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) app_filter_variant = NULL;
g_autoptr(GVariant) oars_filter_variant = NULL;
g_autoptr(GVariant) allow_user_installation_variant = NULL;
g_autoptr(GVariant) allow_system_installation_variant = NULL;
g_autoptr(GVariant) app_filter_result_variant = NULL;
g_autoptr(GVariant) oars_filter_result_variant = NULL;
g_autoptr(GVariant) allow_user_installation_result_variant = NULL;
g_autoptr(GVariant) allow_system_installation_result_variant = NULL;
g_autoptr(GError) local_error = NULL;
g_return_val_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection), FALSE);
g_return_val_if_fail (app_filter != NULL, FALSE);
g_return_val_if_fail (app_filter->ref_count >= 1, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (connection == NULL)
connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, cancellable, error);
if (connection == NULL)
return FALSE;
object_path = accounts_find_user_by_id (connection, user_id,
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE),
cancellable, error);
if (object_path == NULL)
return FALSE;
app_filter_variant = _mct_app_filter_build_app_filter_variant (app_filter);
oars_filter_variant = g_variant_new ("(s@a{ss})", "oars-1.1",
app_filter->oars_ratings);
allow_user_installation_variant = g_variant_new_boolean (app_filter->allow_user_installation);
allow_system_installation_variant = g_variant_new_boolean (app_filter->allow_system_installation);
app_filter_result_variant =
g_dbus_connection_call_sync (connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"AppFilter",
g_steal_pointer (&app_filter_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
oars_filter_result_variant =
g_dbus_connection_call_sync (connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"OarsFilter",
g_steal_pointer (&oars_filter_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
allow_user_installation_result_variant =
g_dbus_connection_call_sync (connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"AllowUserInstallation",
g_steal_pointer (&allow_user_installation_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
allow_system_installation_result_variant =
g_dbus_connection_call_sync (connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"AllowSystemInstallation",
g_steal_pointer (&allow_system_installation_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
return TRUE;
}
static void set_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable);
typedef struct
{
GDBusConnection *connection; /* (nullable) (owned) */
uid_t user_id;
MctAppFilter *app_filter; /* (owned) */
MctSetAppFilterFlags flags;
} SetAppFilterData;
static void
set_app_filter_data_free (SetAppFilterData *data)
{
g_clear_object (&data->connection);
mct_app_filter_unref (data->app_filter);
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetAppFilterData, set_app_filter_data_free)
/**
* mct_set_app_filter_async:
* @connection: (nullable): a #GDBusConnection to the system bus, or %NULL to
* use the default
* @user_id: ID of the user to set the filter for, typically coming from getuid()
* @app_filter: (transfer none): the app filter to set for the user
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: a #GAsyncReadyCallback
* @user_data: user data to pass to @callback
*
* Asynchronously set the app filter settings for the given @user_id to the
* given @app_filter instance. This will set all fields of the app filter.
*
* @connection should be a connection to the system bus, where accounts-service
* runs. Its provided mostly for testing purposes, or to allow an existing
* connection to be re-used. Pass %NULL to use the default connection.
*
* On failure, an #MctAppFilterError, a #GDBusError or a #GIOError will be
* returned. The users app filter settings will be left in an undefined state.
*
* Since: 0.3.0
*/
void
mct_set_app_filter_async (GDBusConnection *connection,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(SetAppFilterData) data = NULL;
g_return_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection));
g_return_if_fail (app_filter != NULL);
g_return_if_fail (app_filter->ref_count >= 1);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, mct_set_app_filter_async);
data = g_new0 (SetAppFilterData, 1);
data->connection = (connection != NULL) ? g_object_ref (connection) : NULL;
data->user_id = user_id;
data->app_filter = mct_app_filter_ref (app_filter);
data->flags = flags;
g_task_set_task_data (task, g_steal_pointer (&data),
(GDestroyNotify) set_app_filter_data_free);
g_task_run_in_thread (task, set_app_filter_thread_cb);
}
static void
set_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
gboolean success;
SetAppFilterData *data = task_data;
g_autoptr(GError) local_error = NULL;
success = mct_set_app_filter (data->connection, data->user_id,
data->app_filter, data->flags,
cancellable, &local_error);
if (local_error != NULL)
g_task_return_error (task, g_steal_pointer (&local_error));
else
g_task_return_boolean (task, success);
}
/**
* mct_set_app_filter_finish:
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finish an asynchronous operation to set the app filter for a user, started
* with mct_set_app_filter_async().
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: 0.2.0
*/
gboolean
mct_set_app_filter_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, NULL), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
/* /*
* Actual implementation of #MctAppFilterBuilder. * Actual implementation of #MctAppFilterBuilder.
* *

View File

@ -37,7 +37,8 @@ G_BEGIN_DECLS
* a user is inconsistent or invalid * a user is inconsistent or invalid
* @MCT_APP_FILTER_ERROR_DISABLED: App filtering is disabled for all users (Since: 0.3.0) * @MCT_APP_FILTER_ERROR_DISABLED: App filtering is disabled for all users (Since: 0.3.0)
* *
* Errors which can be returned by mct_get_app_filter_async(). * Errors relating to #MctAppFilter instances, which can be returned by
* mct_manager_get_app_filter_async() (for example).
* *
* Since: 0.2.0 * Since: 0.2.0
*/ */
@ -78,40 +79,6 @@ typedef enum
MCT_APP_FILTER_OARS_VALUE_INTENSE, MCT_APP_FILTER_OARS_VALUE_INTENSE,
} MctAppFilterOarsValue; } MctAppFilterOarsValue;
/**
* MctGetAppFilterFlags:
* @MCT_GET_APP_FILTER_FLAGS_NONE: No flags set.
* @MCT_GET_APP_FILTER_FLAGS_INTERACTIVE: Allow interactive polkit dialogs when
* requesting authorization.
*
* Flags to control the behaviour of mct_get_app_filter() and
* mct_get_app_filter_async().
*
* Since: 0.3.0
*/
typedef enum
{
MCT_GET_APP_FILTER_FLAGS_NONE = 0,
MCT_GET_APP_FILTER_FLAGS_INTERACTIVE,
} MctGetAppFilterFlags;
/**
* MctSetAppFilterFlags:
* @MCT_SET_APP_FILTER_FLAGS_NONE: No flags set.
* @MCT_SET_APP_FILTER_FLAGS_INTERACTIVE: Allow interactive polkit dialogs when
* requesting authorization.
*
* Flags to control the behaviour of mct_set_app_filter() and
* mct_set_app_filter_async().
*
* Since: 0.3.0
*/
typedef enum
{
MCT_SET_APP_FILTER_FLAGS_NONE = 0,
MCT_SET_APP_FILTER_FLAGS_INTERACTIVE,
} MctSetAppFilterFlags;
/** /**
* MctAppFilter: * MctAppFilter:
* *
@ -150,36 +117,6 @@ MctAppFilterOarsValue mct_app_filter_get_oars_value (MctAppFilter *filter,
gboolean mct_app_filter_is_user_installation_allowed (MctAppFilter *filter); gboolean mct_app_filter_is_user_installation_allowed (MctAppFilter *filter);
gboolean mct_app_filter_is_system_installation_allowed (MctAppFilter *filter); gboolean mct_app_filter_is_system_installation_allowed (MctAppFilter *filter);
MctAppFilter *mct_get_app_filter (GDBusConnection *connection,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GError **error);
void mct_get_app_filter_async (GDBusConnection *connection,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MctAppFilter *mct_get_app_filter_finish (GAsyncResult *result,
GError **error);
gboolean mct_set_app_filter (GDBusConnection *connection,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GError **error);
void mct_set_app_filter_async (GDBusConnection *connection,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mct_set_app_filter_finish (GAsyncResult *result,
GError **error);
/** /**
* MctAppFilterBuilder: * MctAppFilterBuilder:
* *

779
libmalcontent/manager.c Normal file
View File

@ -0,0 +1,779 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright © 2019 Endless Mobile, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
* - Philip Withnall <withnall@endlessm.com>
*/
#include "config.h"
#include <glib.h>
#include <glib-object.h>
#include <glib/gi18n-lib.h>
#include <gio/gio.h>
#include <libmalcontent/app-filter.h>
#include <libmalcontent/manager.h>
#include "libmalcontent/app-filter-private.h"
/**
* MctManager:
*
* #MctManager is a top-level management object which is used to query and
* monitor #MctAppFilters for different users.
*
* Since: 0.3.0
*/
struct _MctManager
{
GObject parent_instance;
GDBusConnection *connection; /* (owned) */
};
G_DEFINE_TYPE (MctManager, mct_manager, G_TYPE_OBJECT)
typedef enum
{
PROP_CONNECTION = 1,
} MctManagerProperty;
static GParamSpec *props[PROP_CONNECTION + 1] = { NULL, };
static void
mct_manager_init (MctManager *self)
{
/* Nothing to do here. */
}
static void
mct_manager_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *spec)
{
MctManager *self = MCT_MANAGER (object);
switch ((MctManagerProperty) property_id)
{
case PROP_CONNECTION:
g_value_set_object (value, self->connection);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
break;
}
}
static void
mct_manager_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *spec)
{
MctManager *self = MCT_MANAGER (object);
switch ((MctManagerProperty) property_id)
{
case PROP_CONNECTION:
/* Construct-only. May be %NULL. */
g_assert (self->connection == NULL);
self->connection = g_value_dup_object (value);
g_assert (self->connection != NULL);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec);
break;
}
}
static void
mct_manager_dispose (GObject *object)
{
MctManager *self = MCT_MANAGER (object);
g_clear_object (&self->connection);
G_OBJECT_CLASS (mct_manager_parent_class)->dispose (object);
}
static void
mct_manager_class_init (MctManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = mct_manager_dispose;
object_class->get_property = mct_manager_get_property;
object_class->set_property = mct_manager_set_property;
/**
* MctManager:connection:
*
* A connection to the system bus, where accounts-service runs. Its provided
* mostly for testing purposes, or to allow an existing connection to be
* re-used.
*
* Since: 0.3.0
*/
props[PROP_CONNECTION] = g_param_spec_object ("connection",
"D-Bus Connection",
"A connection to the system bus.",
G_TYPE_DBUS_CONNECTION,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
g_object_class_install_properties (object_class,
G_N_ELEMENTS (props),
props);
}
/**
* mct_manager_new:
* @connection: (transfer none): a #GDBusConnection to use
*
* Create a new #MctManager.
*
* Returns: (transfer full): a new #MctManager
* Since: 0.3.0
*/
MctManager *
mct_manager_new (GDBusConnection *connection)
{
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
return g_object_new (MCT_TYPE_MANAGER,
"connection", connection,
NULL);
}
/**
* _mct_app_filter_build_app_filter_variant:
* @filter: an #MctAppFilter
*
* Build a #GVariant which contains the app filter from @filter, in the format
* used for storing it in AccountsService.
*
* Returns: (transfer floating): a new, floating #GVariant containing the app
* filter
* Since: 0.2.0
*/
static GVariant *
_mct_app_filter_build_app_filter_variant (MctAppFilter *filter)
{
g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("(bas)"));
g_return_val_if_fail (filter != NULL, NULL);
g_return_val_if_fail (filter->ref_count >= 1, NULL);
g_variant_builder_add (&builder, "b",
(filter->app_list_type == MCT_APP_FILTER_LIST_WHITELIST));
g_variant_builder_open (&builder, G_VARIANT_TYPE ("as"));
for (gsize i = 0; filter->app_list[i] != NULL; i++)
g_variant_builder_add (&builder, "s", filter->app_list[i]);
g_variant_builder_close (&builder);
return g_variant_builder_end (&builder);
}
/* Check if @error is a D-Bus remote error matching @expected_error_name. */
static gboolean
bus_remote_error_matches (const GError *error,
const gchar *expected_error_name)
{
g_autofree gchar *error_name = NULL;
if (!g_dbus_error_is_remote_error (error))
return FALSE;
error_name = g_dbus_error_get_remote_error (error);
return g_str_equal (error_name, expected_error_name);
}
/* Convert a #GDBusError into a #MctAppFilter error. */
static GError *
bus_error_to_app_filter_error (const GError *bus_error,
uid_t user_id)
{
if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED) ||
bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.PermissionDenied"))
return g_error_new (MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED,
_("Not allowed to query app filter data for user %u"),
(guint) user_id);
else if (g_error_matches (bus_error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD) ||
bus_remote_error_matches (bus_error, "org.freedesktop.Accounts.Error.Failed"))
return g_error_new (MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_INVALID_USER,
_("User %u does not exist"), (guint) user_id);
else
return g_error_copy (bus_error);
}
/* Find the object path for the given @user_id on the accountsservice D-Bus
* interface, by calling its FindUserById() method. This is a synchronous,
* blocking function. */
static gchar *
accounts_find_user_by_id (GDBusConnection *connection,
uid_t user_id,
gboolean allow_interactive_authorization,
GCancellable *cancellable,
GError **error)
{
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) result_variant = NULL;
g_autoptr(GError) local_error = NULL;
result_variant =
g_dbus_connection_call_sync (connection,
"org.freedesktop.Accounts",
"/org/freedesktop/Accounts",
"org.freedesktop.Accounts",
"FindUserById",
g_variant_new ("(x)", (gint64) user_id),
G_VARIANT_TYPE ("(o)"),
allow_interactive_authorization
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_autoptr(GError) app_filter_error = bus_error_to_app_filter_error (local_error,
user_id);
g_propagate_error (error, g_steal_pointer (&app_filter_error));
return NULL;
}
g_variant_get (result_variant, "(o)", &object_path);
return g_steal_pointer (&object_path);
}
/**
* mct_manager_get_app_filter:
* @self: a #MctManager
* @user_id: ID of the user to query, typically coming from getuid()
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronous version of mct_manager_get_app_filter_async().
*
* Returns: (transfer full): app filter for the queried user
* Since: 0.3.0
*/
MctAppFilter *
mct_manager_get_app_filter (MctManager *self,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GError **error)
{
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) result_variant = NULL;
g_autoptr(GVariant) properties = NULL;
g_autoptr(GError) local_error = NULL;
g_autoptr(MctAppFilter) app_filter = NULL;
gboolean is_whitelist;
g_auto(GStrv) app_list = NULL;
const gchar *content_rating_kind;
g_autoptr(GVariant) oars_variant = NULL;
gboolean allow_user_installation;
gboolean allow_system_installation;
g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
object_path = accounts_find_user_by_id (self->connection, user_id,
(flags & MCT_GET_APP_FILTER_FLAGS_INTERACTIVE),
cancellable, error);
if (object_path == NULL)
return NULL;
result_variant =
g_dbus_connection_call_sync (self->connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"GetAll",
g_variant_new ("(s)", "com.endlessm.ParentalControls.AppFilter"),
G_VARIANT_TYPE ("(a{sv})"),
(flags & MCT_GET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_autoptr(GError) app_filter_error = NULL;
if (g_error_matches (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS))
{
/* o.fd.D.GetAll() will return InvalidArgs errors if
* accountsservice doesnt have the com.endlessm.ParentalControls.AppFilter
* extension interface installed. */
app_filter_error = g_error_new_literal (MCT_APP_FILTER_ERROR,
MCT_APP_FILTER_ERROR_DISABLED,
_("App filtering is globally disabled"));
}
else
{
app_filter_error = bus_error_to_app_filter_error (local_error,
user_id);
}
g_propagate_error (error, g_steal_pointer (&app_filter_error));
return NULL;
}
/* Extract the properties we care about. They may be silently omitted from the
* results if we dont have permission to access them. */
properties = g_variant_get_child_value (result_variant, 0);
if (!g_variant_lookup (properties, "AppFilter", "(b^as)",
&is_whitelist, &app_list))
{
g_set_error (error, MCT_APP_FILTER_ERROR,
MCT_APP_FILTER_ERROR_PERMISSION_DENIED,
_("Not allowed to query app filter data for user %u"),
(guint) user_id);
return NULL;
}
if (!g_variant_lookup (properties, "OarsFilter", "(&s@a{ss})",
&content_rating_kind, &oars_variant))
{
/* Default value. */
content_rating_kind = "oars-1.1";
oars_variant = g_variant_new ("a{ss}", NULL);
}
/* Check that the OARS filter is in a format we support. Currently, thats
* only oars-1.0 and oars-1.1. */
if (!g_str_equal (content_rating_kind, "oars-1.0") &&
!g_str_equal (content_rating_kind, "oars-1.1"))
{
g_set_error (error, MCT_APP_FILTER_ERROR,
MCT_APP_FILTER_ERROR_INVALID_DATA,
_("OARS filter for user %u has an unrecognized kind %s"),
(guint) user_id, content_rating_kind);
return NULL;
}
if (!g_variant_lookup (properties, "AllowUserInstallation", "b",
&allow_user_installation))
{
/* Default value. */
allow_user_installation = TRUE;
}
if (!g_variant_lookup (properties, "AllowSystemInstallation", "b",
&allow_system_installation))
{
/* Default value. */
allow_system_installation = FALSE;
}
/* Success. Create an #MctAppFilter object to contain the results. */
app_filter = g_new0 (MctAppFilter, 1);
app_filter->ref_count = 1;
app_filter->user_id = user_id;
app_filter->app_list = g_steal_pointer (&app_list);
app_filter->app_list_type =
is_whitelist ? MCT_APP_FILTER_LIST_WHITELIST : MCT_APP_FILTER_LIST_BLACKLIST;
app_filter->oars_ratings = g_steal_pointer (&oars_variant);
app_filter->allow_user_installation = allow_user_installation;
app_filter->allow_system_installation = allow_system_installation;
return g_steal_pointer (&app_filter);
}
static void get_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable);
typedef struct
{
uid_t user_id;
MctGetAppFilterFlags flags;
} GetAppFilterData;
static void
get_app_filter_data_free (GetAppFilterData *data)
{
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetAppFilterData, get_app_filter_data_free)
/**
* mct_manager_get_app_filter_async:
* @self: a #MctManager
* @user_id: ID of the user to query, typically coming from getuid()
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: a #GAsyncReadyCallback
* @user_data: user data to pass to @callback
*
* Asynchronously get a snapshot of the app filter settings for the given
* @user_id.
*
* On failure, an #MctAppFilterError, a #GDBusError or a #GIOError will be
* returned.
*
* Since: 0.3.0
*/
void
mct_manager_get_app_filter_async (MctManager *self,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GetAppFilterData) data = NULL;
g_return_if_fail (MCT_IS_MANAGER (self));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, mct_manager_get_app_filter_async);
data = g_new0 (GetAppFilterData, 1);
data->user_id = user_id;
data->flags = flags;
g_task_set_task_data (task, g_steal_pointer (&data),
(GDestroyNotify) get_app_filter_data_free);
g_task_run_in_thread (task, get_app_filter_thread_cb);
}
static void
get_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
g_autoptr(MctAppFilter) filter = NULL;
MctManager *manager = MCT_MANAGER (source_object);
GetAppFilterData *data = task_data;
g_autoptr(GError) local_error = NULL;
filter = mct_manager_get_app_filter (manager, data->user_id,
data->flags,
cancellable, &local_error);
if (local_error != NULL)
g_task_return_error (task, g_steal_pointer (&local_error));
else
g_task_return_pointer (task, g_steal_pointer (&filter),
(GDestroyNotify) mct_app_filter_unref);
}
/**
* mct_manager_get_app_filter_finish:
* @self: a #MctManager
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finish an asynchronous operation to get the app filter for a user, started
* with mct_manager_get_app_filter_async().
*
* Returns: (transfer full): app filter for the queried user
* Since: 0.3.0
*/
MctAppFilter *
mct_manager_get_app_filter_finish (MctManager *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (MCT_IS_MANAGER (self), NULL);
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
/**
* mct_manager_set_app_filter:
* @self: a #MctManager
* @user_id: ID of the user to set the filter for, typically coming from getuid()
* @app_filter: (transfer none): the app filter to set for the user
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @error: return location for a #GError, or %NULL
*
* Synchronous version of mct_manager_set_app_filter_async().
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: 0.3.0
*/
gboolean
mct_manager_set_app_filter (MctManager *self,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GError **error)
{
g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) app_filter_variant = NULL;
g_autoptr(GVariant) oars_filter_variant = NULL;
g_autoptr(GVariant) allow_user_installation_variant = NULL;
g_autoptr(GVariant) allow_system_installation_variant = NULL;
g_autoptr(GVariant) app_filter_result_variant = NULL;
g_autoptr(GVariant) oars_filter_result_variant = NULL;
g_autoptr(GVariant) allow_user_installation_result_variant = NULL;
g_autoptr(GVariant) allow_system_installation_result_variant = NULL;
g_autoptr(GError) local_error = NULL;
g_autoptr(GDBusConnection) connection = NULL;
g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
g_return_val_if_fail (app_filter != NULL, FALSE);
g_return_val_if_fail (app_filter->ref_count >= 1, FALSE);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
object_path = accounts_find_user_by_id (self->connection, user_id,
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE),
cancellable, error);
if (object_path == NULL)
return FALSE;
app_filter_variant = _mct_app_filter_build_app_filter_variant (app_filter);
oars_filter_variant = g_variant_new ("(s@a{ss})", "oars-1.1",
app_filter->oars_ratings);
allow_user_installation_variant = g_variant_new_boolean (app_filter->allow_user_installation);
allow_system_installation_variant = g_variant_new_boolean (app_filter->allow_system_installation);
app_filter_result_variant =
g_dbus_connection_call_sync (self->connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"AppFilter",
g_steal_pointer (&app_filter_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
oars_filter_result_variant =
g_dbus_connection_call_sync (self->connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"OarsFilter",
g_steal_pointer (&oars_filter_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
allow_user_installation_result_variant =
g_dbus_connection_call_sync (self->connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"AllowUserInstallation",
g_steal_pointer (&allow_user_installation_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
allow_system_installation_result_variant =
g_dbus_connection_call_sync (self->connection,
"org.freedesktop.Accounts",
object_path,
"org.freedesktop.DBus.Properties",
"Set",
g_variant_new ("(ssv)",
"com.endlessm.ParentalControls.AppFilter",
"AllowSystemInstallation",
g_steal_pointer (&allow_system_installation_variant)),
G_VARIANT_TYPE ("()"),
(flags & MCT_SET_APP_FILTER_FLAGS_INTERACTIVE)
? G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION
: G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout, ms */
cancellable,
&local_error);
if (local_error != NULL)
{
g_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
return TRUE;
}
static void set_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable);
typedef struct
{
uid_t user_id;
MctAppFilter *app_filter; /* (owned) */
MctSetAppFilterFlags flags;
} SetAppFilterData;
static void
set_app_filter_data_free (SetAppFilterData *data)
{
mct_app_filter_unref (data->app_filter);
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (SetAppFilterData, set_app_filter_data_free)
/**
* mct_manager_set_app_filter_async:
* @self: a #MctManager
* @user_id: ID of the user to set the filter for, typically coming from getuid()
* @app_filter: (transfer none): the app filter to set for the user
* @flags: flags to affect the behaviour of the call
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: a #GAsyncReadyCallback
* @user_data: user data to pass to @callback
*
* Asynchronously set the app filter settings for the given @user_id to the
* given @app_filter instance. This will set all fields of the app filter.
*
* On failure, an #MctAppFilterError, a #GDBusError or a #GIOError will be
* returned. The users app filter settings will be left in an undefined state.
*
* Since: 0.3.0
*/
void
mct_manager_set_app_filter_async (MctManager *self,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(SetAppFilterData) data = NULL;
g_return_if_fail (MCT_IS_MANAGER (self));
g_return_if_fail (app_filter != NULL);
g_return_if_fail (app_filter->ref_count >= 1);
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, mct_manager_set_app_filter_async);
data = g_new0 (SetAppFilterData, 1);
data->user_id = user_id;
data->app_filter = mct_app_filter_ref (app_filter);
data->flags = flags;
g_task_set_task_data (task, g_steal_pointer (&data),
(GDestroyNotify) set_app_filter_data_free);
g_task_run_in_thread (task, set_app_filter_thread_cb);
}
static void
set_app_filter_thread_cb (GTask *task,
gpointer source_object,
gpointer task_data,
GCancellable *cancellable)
{
gboolean success;
MctManager *manager = MCT_MANAGER (source_object);
SetAppFilterData *data = task_data;
g_autoptr(GError) local_error = NULL;
success = mct_manager_set_app_filter (manager, data->user_id,
data->app_filter, data->flags,
cancellable, &local_error);
if (local_error != NULL)
g_task_return_error (task, g_steal_pointer (&local_error));
else
g_task_return_boolean (task, success);
}
/**
* mct_manager_set_app_filter_finish:
* @self: a #MctManager
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finish an asynchronous operation to set the app filter for a user, started
* with mct_manager_set_app_filter_async().
*
* Returns: %TRUE on success, %FALSE otherwise
* Since: 0.3.0
*/
gboolean
mct_manager_set_app_filter_finish (MctManager *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (MCT_IS_MANAGER (self), FALSE);
g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}

103
libmalcontent/manager.h Normal file
View File

@ -0,0 +1,103 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright © 2019 Endless Mobile, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors:
* - Philip Withnall <withnall@endlessm.com>
*/
#pragma once
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <libmalcontent/app-filter.h>
G_BEGIN_DECLS
/**
* MctGetAppFilterFlags:
* @MCT_GET_APP_FILTER_FLAGS_NONE: No flags set.
* @MCT_GET_APP_FILTER_FLAGS_INTERACTIVE: Allow interactive polkit dialogs when
* requesting authorization.
*
* Flags to control the behaviour of mct_manager_get_app_filter() and
* mct_manager_get_app_filter_async().
*
* Since: 0.3.0
*/
typedef enum
{
MCT_GET_APP_FILTER_FLAGS_NONE = 0,
MCT_GET_APP_FILTER_FLAGS_INTERACTIVE,
} MctGetAppFilterFlags;
/**
* MctSetAppFilterFlags:
* @MCT_SET_APP_FILTER_FLAGS_NONE: No flags set.
* @MCT_SET_APP_FILTER_FLAGS_INTERACTIVE: Allow interactive polkit dialogs when
* requesting authorization.
*
* Flags to control the behaviour of mct_manager_set_app_filter() and
* mct_manager_set_app_filter_async().
*
* Since: 0.3.0
*/
typedef enum
{
MCT_SET_APP_FILTER_FLAGS_NONE = 0,
MCT_SET_APP_FILTER_FLAGS_INTERACTIVE,
} MctSetAppFilterFlags;
#define MCT_TYPE_MANAGER mct_manager_get_type ()
G_DECLARE_FINAL_TYPE (MctManager, mct_manager, MCT, MANAGER, GObject)
MctManager *mct_manager_new (GDBusConnection *connection);
MctAppFilter *mct_manager_get_app_filter (MctManager *self,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GError **error);
void mct_manager_get_app_filter_async (MctManager *self,
uid_t user_id,
MctGetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
MctAppFilter *mct_manager_get_app_filter_finish (MctManager *self,
GAsyncResult *result,
GError **error);
gboolean mct_manager_set_app_filter (MctManager *self,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GError **error);
void mct_manager_set_app_filter_async (MctManager *self,
uid_t user_id,
MctAppFilter *app_filter,
MctSetAppFilterFlags flags,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mct_manager_set_app_filter_finish (MctManager *self,
GAsyncResult *result,
GError **error);
G_END_DECLS

View File

@ -2,9 +2,14 @@ libmalcontent_api_version = '0'
libmalcontent_api_name = 'malcontent-' + libmalcontent_api_version libmalcontent_api_name = 'malcontent-' + libmalcontent_api_version
libmalcontent_sources = [ libmalcontent_sources = [
'app-filter.c', 'app-filter.c',
'manager.c',
] ]
libmalcontent_headers = [ libmalcontent_headers = [
'app-filter.h', 'app-filter.h',
'manager.h',
]
libmalcontent_private_headers = [
'app-filter-private.h',
] ]
libmalcontent_public_deps = [ libmalcontent_public_deps = [
@ -20,7 +25,7 @@ libmalcontent_private_deps = [
libmalcontent_include_subdir = join_paths(libmalcontent_api_name, 'libmalcontent') libmalcontent_include_subdir = join_paths(libmalcontent_api_name, 'libmalcontent')
libmalcontent = library(libmalcontent_api_name, libmalcontent = library(libmalcontent_api_name,
libmalcontent_sources + libmalcontent_headers, libmalcontent_sources + libmalcontent_headers + libmalcontent_private_headers,
dependencies: libmalcontent_public_deps + libmalcontent_private_deps, dependencies: libmalcontent_public_deps + libmalcontent_private_deps,
include_directories: root_inc, include_directories: root_inc,
install: true, install: true,
@ -49,7 +54,7 @@ pkgconfig.generate(
) )
gnome.generate_gir(libmalcontent, gnome.generate_gir(libmalcontent,
sources: libmalcontent_sources + libmalcontent_headers, sources: libmalcontent_sources + libmalcontent_headers + libmalcontent_private_headers,
nsversion: libmalcontent_api_version, nsversion: libmalcontent_api_version,
namespace: 'Malcontent', namespace: 'Malcontent',
symbol_prefix: 'mct_', symbol_prefix: 'mct_',
@ -57,6 +62,7 @@ gnome.generate_gir(libmalcontent,
export_packages: 'libmalcontent', export_packages: 'libmalcontent',
includes: ['GObject-2.0', 'Gio-2.0'], includes: ['GObject-2.0', 'Gio-2.0'],
install: true, install: true,
dependencies: libmalcontent_dep,
) )
subdir('tests') subdir('tests')

View File

@ -26,6 +26,7 @@
#include <gio/gdesktopappinfo.h> #include <gio/gdesktopappinfo.h>
#include <gio/gio.h> #include <gio/gio.h>
#include <libmalcontent/app-filter.h> #include <libmalcontent/app-filter.h>
#include <libmalcontent/manager.h>
#include <libglib-testing/dbus-queue.h> #include <libglib-testing/dbus-queue.h>
#include <locale.h> #include <locale.h>
#include <string.h> #include <string.h>
@ -422,6 +423,7 @@ typedef struct
GtDBusQueue *queue; /* (owned) */ GtDBusQueue *queue; /* (owned) */
uid_t valid_uid; uid_t valid_uid;
uid_t missing_uid; uid_t missing_uid;
MctManager *manager; /* (owned) */
} BusFixture; } BusFixture;
static void static void
@ -452,12 +454,15 @@ bus_set_up (BusFixture *fixture,
(GDBusInterfaceInfo *) &accounts_interface_info, (GDBusInterfaceInfo *) &accounts_interface_info,
&local_error); &local_error);
g_assert_no_error (local_error); g_assert_no_error (local_error);
fixture->manager = mct_manager_new (gt_dbus_queue_get_client_connection (fixture->queue));
} }
static void static void
bus_tear_down (BusFixture *fixture, bus_tear_down (BusFixture *fixture,
gconstpointer test_data) gconstpointer test_data)
{ {
g_clear_object (&fixture->manager);
gt_dbus_queue_disconnect (fixture->queue, TRUE); gt_dbus_queue_disconnect (fixture->queue, TRUE);
g_clear_pointer (&fixture->queue, gt_dbus_queue_free); g_clear_pointer (&fixture->queue, gt_dbus_queue_free);
} }
@ -477,7 +482,7 @@ async_result_cb (GObject *obj,
/* Generic mock accountsservice implementation which returns the properties /* Generic mock accountsservice implementation which returns the properties
* given in #GetAppFilterData.properties if queried for a UID matching * given in #GetAppFilterData.properties if queried for a UID matching
* #GetAppFilterData.expected_uid. Intended to be used for writing successful * #GetAppFilterData.expected_uid. Intended to be used for writing successful
* mct_get_app_filter() tests returning a variety of values. */ * mct_manager_get_app_filter() tests returning a variety of values. */
typedef struct typedef struct
{ {
uid_t expected_uid; uid_t expected_uid;
@ -553,21 +558,21 @@ test_app_filter_bus_get (BusFixture *fixture,
{ {
g_autoptr(GAsyncResult) result = NULL; g_autoptr(GAsyncResult) result = NULL;
mct_get_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_get_app_filter_async (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
app_filter = mct_get_app_filter_finish (result, &local_error); app_filter = mct_manager_get_app_filter_finish (fixture->manager, result, &local_error);
} }
else else
{ {
app_filter = mct_get_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), app_filter = mct_manager_get_app_filter (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
} }
g_assert_no_error (local_error); g_assert_no_error (local_error);
@ -608,10 +613,10 @@ test_app_filter_bus_get_whitelist (BusFixture *fixture,
gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb, gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb,
(gpointer) &get_app_filter_data); (gpointer) &get_app_filter_data);
app_filter = mct_get_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), app_filter = mct_manager_get_app_filter (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
g_assert_no_error (local_error); g_assert_no_error (local_error);
g_assert_nonnull (app_filter); g_assert_nonnull (app_filter);
@ -659,10 +664,10 @@ test_app_filter_bus_get_all_oars_values (BusFixture *fixture,
gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb, gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb,
(gpointer) &get_app_filter_data); (gpointer) &get_app_filter_data);
app_filter = mct_get_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), app_filter = mct_manager_get_app_filter (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
g_assert_no_error (local_error); g_assert_no_error (local_error);
g_assert_nonnull (app_filter); g_assert_nonnull (app_filter);
@ -707,10 +712,10 @@ test_app_filter_bus_get_defaults (BusFixture *fixture,
gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb, gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb,
(gpointer) &get_app_filter_data); (gpointer) &get_app_filter_data);
app_filter = mct_get_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), app_filter = mct_manager_get_app_filter (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
g_assert_no_error (local_error); g_assert_no_error (local_error);
g_assert_nonnull (app_filter); g_assert_nonnull (app_filter);
@ -725,8 +730,8 @@ test_app_filter_bus_get_defaults (BusFixture *fixture,
g_assert_false (mct_app_filter_is_system_installation_allowed (app_filter)); g_assert_false (mct_app_filter_is_system_installation_allowed (app_filter));
} }
/* Test that mct_get_app_filter() returns an appropriate error if the mock D-Bus /* Test that mct_manager_get_app_filter() returns an appropriate error if the
* service reports that the given user cannot be found. * mock D-Bus service reports that the given user cannot be found.
* *
* The mock D-Bus replies are generated inline. */ * The mock D-Bus replies are generated inline. */
static void static void
@ -739,10 +744,10 @@ test_app_filter_bus_get_error_invalid_user (BusFixture *fixture,
g_autofree gchar *error_message = NULL; g_autofree gchar *error_message = NULL;
g_autoptr(MctAppFilter) app_filter = NULL; g_autoptr(MctAppFilter) app_filter = NULL;
mct_get_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_get_app_filter_async (fixture->manager,
fixture->missing_uid, fixture->missing_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
/* Handle the FindUserById() call and claim the user doesnt exist. */ /* Handle the FindUserById() call and claim the user doesnt exist. */
gint64 user_id; gint64 user_id;
@ -761,16 +766,17 @@ test_app_filter_bus_get_error_invalid_user (BusFixture *fixture,
/* Get the get_app_filter() result. */ /* Get the get_app_filter() result. */
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
app_filter = mct_get_app_filter_finish (result, &local_error); app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
&local_error);
g_assert_error (local_error, g_assert_error (local_error,
MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_INVALID_USER); MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_INVALID_USER);
g_assert_null (app_filter); g_assert_null (app_filter);
} }
/* Test that mct_get_app_filter() returns an appropriate error if the mock D-Bus /* Test that mct_manager_get_app_filter() returns an appropriate error if the
* service reports that the properties of the given user cant be accessed due * mock D-Bus service reports that the properties of the given user cant be
* to permissions. * accessed due to permissions.
* *
* The mock D-Bus replies are generated inline. */ * The mock D-Bus replies are generated inline. */
static void static void
@ -784,10 +790,10 @@ test_app_filter_bus_get_error_permission_denied (BusFixture *fixture,
g_autofree gchar *object_path = NULL; g_autofree gchar *object_path = NULL;
g_autoptr(MctAppFilter) app_filter = NULL; g_autoptr(MctAppFilter) app_filter = NULL;
mct_get_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_get_app_filter_async (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
/* Handle the FindUserById() call. */ /* Handle the FindUserById() call. */
gint64 user_id; gint64 user_id;
@ -817,16 +823,17 @@ test_app_filter_bus_get_error_permission_denied (BusFixture *fixture,
/* Get the get_app_filter() result. */ /* Get the get_app_filter() result. */
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
app_filter = mct_get_app_filter_finish (result, &local_error); app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
&local_error);
g_assert_error (local_error, g_assert_error (local_error,
MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED); MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED);
g_assert_null (app_filter); g_assert_null (app_filter);
} }
/* Test that mct_get_app_filter() returns an appropriate error if the mock D-Bus /* Test that mct_manager_get_app_filter() returns an appropriate error if the
* service replies with no app filter properties (implying that it hasnt sent * mock D-Bus service replies with no app filter properties (implying that it
* the property values because of permissions). * hasnt sent the property values because of permissions).
* *
* The mock D-Bus replies are generated inline. */ * The mock D-Bus replies are generated inline. */
static void static void
@ -840,10 +847,10 @@ test_app_filter_bus_get_error_permission_denied_missing (BusFixture *fixture,
g_autofree gchar *object_path = NULL; g_autofree gchar *object_path = NULL;
g_autoptr(MctAppFilter) app_filter = NULL; g_autoptr(MctAppFilter) app_filter = NULL;
mct_get_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_get_app_filter_async (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
/* Handle the FindUserById() call. */ /* Handle the FindUserById() call. */
gint64 user_id; gint64 user_id;
@ -874,15 +881,16 @@ test_app_filter_bus_get_error_permission_denied_missing (BusFixture *fixture,
/* Get the get_app_filter() result. */ /* Get the get_app_filter() result. */
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
app_filter = mct_get_app_filter_finish (result, &local_error); app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
&local_error);
g_assert_error (local_error, g_assert_error (local_error,
MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED); MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED);
g_assert_null (app_filter); g_assert_null (app_filter);
} }
/* Test that mct_get_app_filter() returns an error if the mock D-Bus service /* Test that mct_manager_get_app_filter() returns an error if the mock D-Bus
* reports an unrecognised error. * service reports an unrecognised error.
* *
* The mock D-Bus replies are generated inline. */ * The mock D-Bus replies are generated inline. */
static void static void
@ -894,10 +902,10 @@ test_app_filter_bus_get_error_unknown (BusFixture *fixture,
g_autoptr(GDBusMethodInvocation) invocation = NULL; g_autoptr(GDBusMethodInvocation) invocation = NULL;
g_autoptr(MctAppFilter) app_filter = NULL; g_autoptr(MctAppFilter) app_filter = NULL;
mct_get_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_get_app_filter_async (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
/* Handle the FindUserById() call and return a bogus error. */ /* Handle the FindUserById() call and return a bogus error. */
gint64 user_id; gint64 user_id;
@ -918,16 +926,17 @@ test_app_filter_bus_get_error_unknown (BusFixture *fixture,
/* Get the get_app_filter() result. */ /* Get the get_app_filter() result. */
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
app_filter = mct_get_app_filter_finish (result, &local_error); app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
&local_error);
/* We dont actually care what error is actually used here. */ /* We dont actually care what error is actually used here. */
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
g_assert_null (app_filter); g_assert_null (app_filter);
} }
/* Test that mct_get_app_filter() returns an error if the mock D-Bus service /* Test that mct_manager_get_app_filter() returns an error if the mock D-Bus
* reports an unknown interface, which means that parental controls are not * service reports an unknown interface, which means that parental controls are
* installed properly. * not installed properly.
* *
* The mock D-Bus replies are generated inline. */ * The mock D-Bus replies are generated inline. */
static void static void
@ -941,10 +950,10 @@ test_app_filter_bus_get_error_disabled (BusFixture *fixture,
g_autofree gchar *object_path = NULL; g_autofree gchar *object_path = NULL;
g_autoptr(MctAppFilter) app_filter = NULL; g_autoptr(MctAppFilter) app_filter = NULL;
mct_get_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_get_app_filter_async (fixture->manager,
fixture->valid_uid, fixture->valid_uid,
MCT_GET_APP_FILTER_FLAGS_NONE, NULL, MCT_GET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
/* Handle the FindUserById() call. */ /* Handle the FindUserById() call. */
gint64 user_id; gint64 user_id;
@ -975,7 +984,8 @@ test_app_filter_bus_get_error_disabled (BusFixture *fixture,
/* Get the get_app_filter() result. */ /* Get the get_app_filter() result. */
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
app_filter = mct_get_app_filter_finish (result, &local_error); app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
&local_error);
g_assert_error (local_error, g_assert_error (local_error,
MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_DISABLED); MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_DISABLED);
@ -1132,29 +1142,30 @@ test_app_filter_bus_set (BusFixture *fixture,
{ {
g_autoptr(GAsyncResult) result = NULL; g_autoptr(GAsyncResult) result = NULL;
mct_set_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_set_app_filter_async (fixture->manager,
fixture->valid_uid, app_filter, fixture->valid_uid, app_filter,
MCT_SET_APP_FILTER_FLAGS_NONE, NULL, MCT_SET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
success = mct_set_app_filter_finish (result, &local_error); success = mct_manager_set_app_filter_finish (fixture->manager, result,
&local_error);
} }
else else
{ {
success = mct_set_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), success = mct_manager_set_app_filter (fixture->manager,
fixture->valid_uid, app_filter, fixture->valid_uid, app_filter,
MCT_SET_APP_FILTER_FLAGS_NONE, NULL, MCT_SET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
} }
g_assert_no_error (local_error); g_assert_no_error (local_error);
g_assert_true (success); g_assert_true (success);
} }
/* Test that mct_set_app_filter() returns an appropriate error if the mock D-Bus /* Test that mct_manager__set_app_filter() returns an appropriate error if the
* service reports that the given user cannot be found. * mock D-Bus service reports that the given user cannot be found.
* *
* The mock D-Bus replies are generated inline. */ * The mock D-Bus replies are generated inline. */
static void static void
@ -1172,10 +1183,10 @@ test_app_filter_bus_set_error_invalid_user (BusFixture *fixture,
/* Use the default app filter. */ /* Use the default app filter. */
app_filter = mct_app_filter_builder_end (&builder); app_filter = mct_app_filter_builder_end (&builder);
mct_set_app_filter_async (gt_dbus_queue_get_client_connection (fixture->queue), mct_manager_set_app_filter_async (fixture->manager,
fixture->missing_uid, app_filter, fixture->missing_uid, app_filter,
MCT_SET_APP_FILTER_FLAGS_NONE, NULL, MCT_SET_APP_FILTER_FLAGS_NONE, NULL,
async_result_cb, &result); async_result_cb, &result);
/* Handle the FindUserById() call and claim the user doesnt exist. */ /* Handle the FindUserById() call and claim the user doesnt exist. */
gint64 user_id; gint64 user_id;
@ -1194,15 +1205,17 @@ test_app_filter_bus_set_error_invalid_user (BusFixture *fixture,
/* Get the set_app_filter() result. */ /* Get the set_app_filter() result. */
while (result == NULL) while (result == NULL)
g_main_context_iteration (NULL, TRUE); g_main_context_iteration (NULL, TRUE);
success = mct_set_app_filter_finish (result, &local_error); success = mct_manager_set_app_filter_finish (fixture->manager, result,
&local_error);
g_assert_error (local_error, g_assert_error (local_error,
MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_INVALID_USER); MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_INVALID_USER);
g_assert_false (success); g_assert_false (success);
} }
/* Test that mct_set_app_filter() returns an appropriate error if the mock D-Bus /* Test that mct_manager_set_app_filter() returns an appropriate error if the
* service replies with a permission denied error when setting properties. * mock D-Bus service replies with a permission denied error when setting
* properties.
* *
* The mock D-Bus replies are generated in set_app_filter_server_cb(). */ * The mock D-Bus replies are generated in set_app_filter_server_cb(). */
static void static void
@ -1227,18 +1240,18 @@ test_app_filter_bus_set_error_permission_denied (BusFixture *fixture,
gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb, gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb,
(gpointer) &set_app_filter_data); (gpointer) &set_app_filter_data);
success = mct_set_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), success = mct_manager_set_app_filter (fixture->manager,
fixture->valid_uid, app_filter, fixture->valid_uid, app_filter,
MCT_SET_APP_FILTER_FLAGS_NONE, NULL, MCT_SET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
g_assert_error (local_error, g_assert_error (local_error,
MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED); MCT_APP_FILTER_ERROR, MCT_APP_FILTER_ERROR_PERMISSION_DENIED);
g_assert_false (success); g_assert_false (success);
} }
/* Test that mct_set_app_filter() returns an error if the mock D-Bus service /* Test that mct_manager_set_app_filter() returns an error if the mock D-Bus
* reports an unrecognised error. * service reports an unrecognised error.
* *
* The mock D-Bus replies are generated in set_app_filter_server_cb(). */ * The mock D-Bus replies are generated in set_app_filter_server_cb(). */
static void static void
@ -1265,17 +1278,17 @@ test_app_filter_bus_set_error_unknown (BusFixture *fixture,
gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb, gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb,
(gpointer) &set_app_filter_data); (gpointer) &set_app_filter_data);
success = mct_set_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), success = mct_manager_set_app_filter (fixture->manager,
fixture->valid_uid, app_filter, fixture->valid_uid, app_filter,
MCT_SET_APP_FILTER_FLAGS_NONE, NULL, MCT_SET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR); g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
g_assert_false (success); g_assert_false (success);
} }
/* Test that mct_set_app_filter() returns an error if the mock D-Bus service /* Test that mct_manager_set_app_filter() returns an error if the mock D-Bus
* reports an InvalidArgs error with a given one of its Set() calls. * service reports an InvalidArgs error with a given one of its Set() calls.
* *
* @test_data contains a property index encoded with GINT_TO_POINTER(), * @test_data contains a property index encoded with GINT_TO_POINTER(),
* indicating which Set() call to return the error on, since the calls are made * indicating which Set() call to return the error on, since the calls are made
@ -1308,10 +1321,10 @@ test_app_filter_bus_set_error_invalid_property (BusFixture *fixture,
gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb, gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb,
(gpointer) &set_app_filter_data); (gpointer) &set_app_filter_data);
success = mct_set_app_filter (gt_dbus_queue_get_client_connection (fixture->queue), success = mct_manager_set_app_filter (fixture->manager,
fixture->valid_uid, app_filter, fixture->valid_uid, app_filter,
MCT_SET_APP_FILTER_FLAGS_NONE, NULL, MCT_SET_APP_FILTER_FLAGS_NONE, NULL,
&local_error); &local_error);
g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS); g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
g_assert_false (success); g_assert_false (success);

View File

@ -22,7 +22,7 @@ import pwd
import sys import sys
import gi import gi
gi.require_version('Malcontent', '0') # noqa gi.require_version('Malcontent', '0') # noqa
from gi.repository import Malcontent, GLib from gi.repository import Malcontent, GLib, Gio
# Exit codes, which are a documented part of the API. # Exit codes, which are a documented part of the API.
@ -42,8 +42,10 @@ def __get_app_filter(user_id, interactive):
else: else:
flags = Malcontent.GetAppFilterFlags.NONE flags = Malcontent.GetAppFilterFlags.NONE
return Malcontent.get_app_filter( connection = Gio.bus_get_sync(Gio.BusType.SYSTEM)
connection=None, user_id=user_id, manager = Malcontent.Manager.new(connection)
return manager.get_app_filter(
user_id=user_id,
flags=flags, cancellable=None) flags=flags, cancellable=None)
@ -68,8 +70,10 @@ def __set_app_filter(user_id, app_filter, interactive):
else: else:
flags = Malcontent.GetAppFilterFlags.NONE flags = Malcontent.GetAppFilterFlags.NONE
Malcontent.set_app_filter( connection = Gio.bus_get_sync(Gio.BusType.SYSTEM)
connection=None, user_id=user_id, app_filter=app_filter, manager = Malcontent.Manager.new(connection)
manager.set_app_filter(
user_id=user_id, app_filter=app_filter,
flags=flags, cancellable=None) flags=flags, cancellable=None)