libmalcontent: Add a SessionLimits interface for time-limited sessions
This is another extension interface on accountsservice which stores information about time and usage limits on the user session. Currently, only a ‘daily schedule’ limit (or no limit) is supported, but additional types and combinations of limits can be supported in future. The daily schedule limit allows using the computer between a certain start time and end time each day (the same each day). The user will be kicked out of their session when the end time is reached, if they haven’t already logged out. This includes the getters for the new data, polkit rules for accessing it, and some documentation. Changes to `malcontent-client` to support session limits, setters, and unit tests will all follow. Signed-off-by: Philip Withnall <withnall@endlessm.com>
This commit is contained in:
parent
9dcaa10253
commit
5e49cb7831
|
@ -0,0 +1,50 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
||||||
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||||
|
<node>
|
||||||
|
<interface name="com.endlessm.ParentalControls.SessionLimits">
|
||||||
|
|
||||||
|
<annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
|
||||||
|
|
||||||
|
<annotation name="org.freedesktop.Accounts.Authentication.ChangeOwn"
|
||||||
|
value="com.endlessm.ParentalControls.SessionLimits.ChangeOwn"/>
|
||||||
|
<annotation name="org.freedesktop.Accounts.Authentication.ReadOwn"
|
||||||
|
value="com.endlessm.ParentalControls.SessionLimits.ReadOwn"/>
|
||||||
|
<annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
|
||||||
|
value="com.endlessm.ParentalControls.SessionLimits.ChangeAny"/>
|
||||||
|
<annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
|
||||||
|
value="com.endlessm.ParentalControls.SessionLimits.ReadAny"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
LimitType:
|
||||||
|
|
||||||
|
The type of session limit applied to the user, as an enumerated type.
|
||||||
|
Currently, the following values are supported, but more values may be
|
||||||
|
added in future.
|
||||||
|
- `0`: No filter enabled. The user is not limited in the times or
|
||||||
|
durations of their sessions.
|
||||||
|
- `1`: Daily schedule. The user is limited to using the computer between
|
||||||
|
a fixed start and end time each day, as set in the `DailySchedule`
|
||||||
|
property.
|
||||||
|
-->
|
||||||
|
<property name="LimitType" type="u" access="readwrite">
|
||||||
|
<annotation name="org.freedesktop.Accounts.DefaultValue" value="0"/>
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
DailySchedule:
|
||||||
|
|
||||||
|
A daily schedule to limit the user’s computer use. This is a two-tuple of
|
||||||
|
a start time and an end time, both given as the number of seconds since
|
||||||
|
midnight. The end time must be greater than the start time, and must be
|
||||||
|
≤ 86400 (the number of seconds in a day). There is no handling of leap
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
This property will be used if `LimitType` is set to `1`, but it must be
|
||||||
|
set to a valid value regardless.
|
||||||
|
-->
|
||||||
|
<property name="DailySchedule" type="(uu)" access="readwrite">
|
||||||
|
<annotation name="org.freedesktop.Accounts.DefaultValue" value="(0, 86400)"/>
|
||||||
|
</property>
|
||||||
|
</interface>
|
||||||
|
</node>
|
|
@ -39,4 +39,44 @@
|
||||||
<allow_active>auth_admin_keep</allow_active>
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
</defaults>
|
</defaults>
|
||||||
</action>
|
</action>
|
||||||
|
|
||||||
|
<action id="com.endlessm.ParentalControls.SessionLimits.ChangeOwn">
|
||||||
|
<description>Change your own session limits</description>
|
||||||
|
<message>Authentication is required to change your session limits.</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>auth_admin_keep</allow_any>
|
||||||
|
<allow_inactive>auth_admin_keep</allow_inactive>
|
||||||
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
|
</defaults>
|
||||||
|
</action>
|
||||||
|
|
||||||
|
<action id="com.endlessm.ParentalControls.SessionLimits.ReadOwn">
|
||||||
|
<description>Read your own session limits</description>
|
||||||
|
<message>Authentication is required to read your session limits.</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>yes</allow_any>
|
||||||
|
<allow_inactive>yes</allow_inactive>
|
||||||
|
<allow_active>yes</allow_active>
|
||||||
|
</defaults>
|
||||||
|
</action>
|
||||||
|
|
||||||
|
<action id="com.endlessm.ParentalControls.SessionLimits.ChangeAny">
|
||||||
|
<description>Change another user’s session limits</description>
|
||||||
|
<message>Authentication is required to change another user’s session limits.</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>auth_admin_keep</allow_any>
|
||||||
|
<allow_inactive>auth_admin_keep</allow_inactive>
|
||||||
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
|
</defaults>
|
||||||
|
</action>
|
||||||
|
|
||||||
|
<action id="com.endlessm.ParentalControls.SessionLimits.ReadAny">
|
||||||
|
<description>Read another user’s session limits</description>
|
||||||
|
<message>Authentication is required to read another user’s session limits.</message>
|
||||||
|
<defaults>
|
||||||
|
<allow_any>auth_admin_keep</allow_any>
|
||||||
|
<allow_inactive>auth_admin_keep</allow_inactive>
|
||||||
|
<allow_active>auth_admin_keep</allow_active>
|
||||||
|
</defaults>
|
||||||
|
</action>
|
||||||
</policyconfig>
|
</policyconfig>
|
|
@ -23,7 +23,9 @@ polkit.addRule(function(action, subject) {
|
||||||
/* Allow administrators to read parental controls (for any account) without
|
/* Allow administrators to read parental controls (for any account) without
|
||||||
* needing an additional polkit authorisation dialogue. */
|
* needing an additional polkit authorisation dialogue. */
|
||||||
if ((action.id == "com.endlessm.ParentalControls.AppFilter.ReadOwn" ||
|
if ((action.id == "com.endlessm.ParentalControls.AppFilter.ReadOwn" ||
|
||||||
action.id == "com.endlessm.ParentalControls.AppFilter.ReadAny") &&
|
action.id == "com.endlessm.ParentalControls.AppFilter.ReadAny" ||
|
||||||
|
action.id == "com.endlessm.ParentalControls.SessionLimits.ReadOwn" ||
|
||||||
|
action.id == "com.endlessm.ParentalControls.SessionLimits.ReadAny") &&
|
||||||
subject.active && subject.local &&
|
subject.active && subject.local &&
|
||||||
subject.isInGroup("sudo")) {
|
subject.isInGroup("sudo")) {
|
||||||
return polkit.Result.YES;
|
return polkit.Result.YES;
|
||||||
|
|
|
@ -6,11 +6,19 @@ i18n.merge_file('com.endlessm.ParentalControls.policy',
|
||||||
install_dir: polkitpolicydir,
|
install_dir: polkitpolicydir,
|
||||||
)
|
)
|
||||||
|
|
||||||
install_data('com.endlessm.ParentalControls.AppFilter.xml',
|
dbus_interfaces = [
|
||||||
|
'com.endlessm.ParentalControls.AppFilter',
|
||||||
|
'com.endlessm.ParentalControls.SessionLimits',
|
||||||
|
]
|
||||||
|
|
||||||
|
foreach dbus_interface: dbus_interfaces
|
||||||
|
filename = dbus_interface + '.xml'
|
||||||
|
install_data(filename,
|
||||||
install_dir: dbusinterfacesdir)
|
install_dir: dbusinterfacesdir)
|
||||||
meson.add_install_script(meson_make_symlink,
|
meson.add_install_script(meson_make_symlink,
|
||||||
join_paths(dbusinterfacesdir, 'com.endlessm.ParentalControls.AppFilter.xml'),
|
join_paths(dbusinterfacesdir, filename),
|
||||||
join_paths(accountsserviceinterfacesdir, 'com.endlessm.ParentalControls.AppFilter.xml'))
|
join_paths(accountsserviceinterfacesdir, filename))
|
||||||
|
endforeach
|
||||||
|
|
||||||
install_data('com.endlessm.ParentalControls.rules',
|
install_data('com.endlessm.ParentalControls.rules',
|
||||||
install_dir: join_paths(get_option('datadir'), 'polkit-1', 'rules.d'))
|
install_dir: join_paths(get_option('datadir'), 'polkit-1', 'rules.d'))
|
||||||
|
|
|
@ -24,3 +24,4 @@
|
||||||
|
|
||||||
#include <libmalcontent/app-filter.h>
|
#include <libmalcontent/app-filter.h>
|
||||||
#include <libmalcontent/manager.h>
|
#include <libmalcontent/manager.h>
|
||||||
|
#include <libmalcontent/session-limits.h>
|
||||||
|
|
|
@ -28,8 +28,10 @@
|
||||||
#include <gio/gio.h>
|
#include <gio/gio.h>
|
||||||
#include <libmalcontent/app-filter.h>
|
#include <libmalcontent/app-filter.h>
|
||||||
#include <libmalcontent/manager.h>
|
#include <libmalcontent/manager.h>
|
||||||
|
#include <libmalcontent/session-limits.h>
|
||||||
|
|
||||||
#include "libmalcontent/app-filter-private.h"
|
#include "libmalcontent/app-filter-private.h"
|
||||||
|
#include "libmalcontent/session-limits-private.h"
|
||||||
|
|
||||||
|
|
||||||
G_DEFINE_QUARK (MctManagerError, mct_manager_error)
|
G_DEFINE_QUARK (MctManagerError, mct_manager_error)
|
||||||
|
@ -866,3 +868,239 @@ mct_manager_set_app_filter_finish (MctManager *self,
|
||||||
|
|
||||||
return g_task_propagate_boolean (G_TASK (result), error);
|
return g_task_propagate_boolean (G_TASK (result), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_manager_get_session_limits:
|
||||||
|
* @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_session_limits_async().
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): session limits for the queried user
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
MctSessionLimits *
|
||||||
|
mct_manager_get_session_limits (MctManager *self,
|
||||||
|
uid_t user_id,
|
||||||
|
MctManagerGetValueFlags 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(MctSessionLimits) session_limits = NULL;
|
||||||
|
guint32 limit_type;
|
||||||
|
guint32 daily_start_time, daily_end_time;
|
||||||
|
|
||||||
|
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_MANAGER_GET_VALUE_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.SessionLimits"),
|
||||||
|
G_VARIANT_TYPE ("(a{sv})"),
|
||||||
|
(flags & MCT_MANAGER_GET_VALUE_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) manager_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 doesn’t have the com.endlessm.ParentalControls.SessionLimits
|
||||||
|
* extension interface installed. */
|
||||||
|
manager_error = g_error_new_literal (MCT_MANAGER_ERROR,
|
||||||
|
MCT_MANAGER_ERROR_DISABLED,
|
||||||
|
_("Session limits are globally disabled"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
manager_error = bus_error_to_manager_error (local_error, user_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_propagate_error (error, g_steal_pointer (&manager_error));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract the properties we care about. They may be silently omitted from the
|
||||||
|
* results if we don’t have permission to access them. */
|
||||||
|
properties = g_variant_get_child_value (result_variant, 0);
|
||||||
|
if (!g_variant_lookup (properties, "LimitType", "u",
|
||||||
|
&limit_type))
|
||||||
|
{
|
||||||
|
g_set_error (error, MCT_MANAGER_ERROR,
|
||||||
|
MCT_MANAGER_ERROR_PERMISSION_DENIED,
|
||||||
|
_("Not allowed to query session limits data for user %u"),
|
||||||
|
(guint) user_id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check that the limit type is something we support. */
|
||||||
|
G_STATIC_ASSERT (sizeof (limit_type) >= sizeof (MctSessionLimitsType));
|
||||||
|
|
||||||
|
if ((guint) limit_type > MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE)
|
||||||
|
{
|
||||||
|
g_set_error (error, MCT_MANAGER_ERROR,
|
||||||
|
MCT_MANAGER_ERROR_INVALID_DATA,
|
||||||
|
_("Session limit for user %u has an unrecognized type ‘%u’"),
|
||||||
|
(guint) user_id, limit_type);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_variant_lookup (properties, "DailySchedule", "(uu)",
|
||||||
|
&daily_start_time, &daily_end_time))
|
||||||
|
{
|
||||||
|
/* Default value. */
|
||||||
|
daily_start_time = 0;
|
||||||
|
daily_end_time = 24 * 60 * 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (daily_start_time >= daily_end_time ||
|
||||||
|
daily_end_time > 24 * 60 * 60)
|
||||||
|
{
|
||||||
|
g_set_error (error, MCT_MANAGER_ERROR,
|
||||||
|
MCT_MANAGER_ERROR_INVALID_DATA,
|
||||||
|
_("Session limit for user %u has invalid daily schedule %u–%u"),
|
||||||
|
(guint) user_id, daily_start_time, daily_end_time);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success. Create an #MctSessionLimits object to contain the results. */
|
||||||
|
session_limits = g_new0 (MctSessionLimits, 1);
|
||||||
|
session_limits->ref_count = 1;
|
||||||
|
session_limits->user_id = user_id;
|
||||||
|
session_limits->limit_type = limit_type;
|
||||||
|
session_limits->daily_start_time = daily_start_time;
|
||||||
|
session_limits->daily_end_time = daily_end_time;
|
||||||
|
|
||||||
|
return g_steal_pointer (&session_limits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_session_limits_thread_cb (GTask *task,
|
||||||
|
gpointer source_object,
|
||||||
|
gpointer task_data,
|
||||||
|
GCancellable *cancellable);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uid_t user_id;
|
||||||
|
MctManagerGetValueFlags flags;
|
||||||
|
} GetSessionLimitsData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_session_limits_data_free (GetSessionLimitsData *data)
|
||||||
|
{
|
||||||
|
g_free (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (GetSessionLimitsData, get_session_limits_data_free)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_manager_get_session_limits_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 session limit settings for the given
|
||||||
|
* @user_id.
|
||||||
|
*
|
||||||
|
* On failure, an #MctManagerError, a #GDBusError or a #GIOError will be
|
||||||
|
* returned via mct_manager_get_session_limits_finish().
|
||||||
|
*
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mct_manager_get_session_limits_async (MctManager *self,
|
||||||
|
uid_t user_id,
|
||||||
|
MctManagerGetValueFlags flags,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr(GTask) task = NULL;
|
||||||
|
g_autoptr(GetSessionLimitsData) 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_session_limits_async);
|
||||||
|
|
||||||
|
data = g_new0 (GetSessionLimitsData, 1);
|
||||||
|
data->user_id = user_id;
|
||||||
|
data->flags = flags;
|
||||||
|
g_task_set_task_data (task, g_steal_pointer (&data),
|
||||||
|
(GDestroyNotify) get_session_limits_data_free);
|
||||||
|
|
||||||
|
g_task_run_in_thread (task, get_session_limits_thread_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_session_limits_thread_cb (GTask *task,
|
||||||
|
gpointer source_object,
|
||||||
|
gpointer task_data,
|
||||||
|
GCancellable *cancellable)
|
||||||
|
{
|
||||||
|
g_autoptr(MctSessionLimits) limits = NULL;
|
||||||
|
MctManager *manager = MCT_MANAGER (source_object);
|
||||||
|
GetSessionLimitsData *data = task_data;
|
||||||
|
g_autoptr(GError) local_error = NULL;
|
||||||
|
|
||||||
|
limits = mct_manager_get_session_limits (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 (&limits),
|
||||||
|
(GDestroyNotify) mct_session_limits_unref);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_manager_get_session_limits_finish:
|
||||||
|
* @self: a #MctManager
|
||||||
|
* @result: a #GAsyncResult
|
||||||
|
* @error: return location for a #GError, or %NULL
|
||||||
|
*
|
||||||
|
* Finish an asynchronous operation to get the session limits for a user,
|
||||||
|
* started with mct_manager_get_session_limits_async().
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): session limits for the queried user
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
MctSessionLimits *
|
||||||
|
mct_manager_get_session_limits_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);
|
||||||
|
}
|
||||||
|
|
|
@ -97,6 +97,7 @@ GQuark mct_manager_error_quark (void);
|
||||||
#define MCT_MANAGER_ERROR mct_manager_error_quark ()
|
#define MCT_MANAGER_ERROR mct_manager_error_quark ()
|
||||||
|
|
||||||
#include <libmalcontent/app-filter.h>
|
#include <libmalcontent/app-filter.h>
|
||||||
|
#include <libmalcontent/session-limits.h>
|
||||||
|
|
||||||
#define MCT_TYPE_MANAGER mct_manager_get_type ()
|
#define MCT_TYPE_MANAGER mct_manager_get_type ()
|
||||||
G_DECLARE_FINAL_TYPE (MctManager, mct_manager, MCT, MANAGER, GObject)
|
G_DECLARE_FINAL_TYPE (MctManager, mct_manager, MCT, MANAGER, GObject)
|
||||||
|
@ -135,4 +136,19 @@ gboolean mct_manager_set_app_filter_finish (MctManager *self,
|
||||||
GAsyncResult *result,
|
GAsyncResult *result,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
|
MctSessionLimits *mct_manager_get_session_limits (MctManager *self,
|
||||||
|
uid_t user_id,
|
||||||
|
MctManagerGetValueFlags flags,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GError **error);
|
||||||
|
void mct_manager_get_session_limits_async (MctManager *self,
|
||||||
|
uid_t user_id,
|
||||||
|
MctManagerGetValueFlags flags,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
MctSessionLimits *mct_manager_get_session_limits_finish (MctManager *self,
|
||||||
|
GAsyncResult *result,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
|
@ -3,14 +3,17 @@ libmalcontent_api_name = 'malcontent-' + libmalcontent_api_version
|
||||||
libmalcontent_sources = [
|
libmalcontent_sources = [
|
||||||
'app-filter.c',
|
'app-filter.c',
|
||||||
'manager.c',
|
'manager.c',
|
||||||
|
'session-limits.c',
|
||||||
]
|
]
|
||||||
libmalcontent_headers = [
|
libmalcontent_headers = [
|
||||||
'app-filter.h',
|
'app-filter.h',
|
||||||
'malcontent.h',
|
'malcontent.h',
|
||||||
'manager.h',
|
'manager.h',
|
||||||
|
'session-limits.h',
|
||||||
]
|
]
|
||||||
libmalcontent_private_headers = [
|
libmalcontent_private_headers = [
|
||||||
'app-filter-private.h',
|
'app-filter-private.h',
|
||||||
|
'session-limits-private.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
libmalcontent_public_deps = [
|
libmalcontent_public_deps = [
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* -*- 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/session-limits.h>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MctSessionLimitsType:
|
||||||
|
* @MCT_SESSION_LIMITS_TYPE_NONE: No session limits are imposed.
|
||||||
|
* @MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE: Sessions are limited to between a
|
||||||
|
* pair of given times each day.
|
||||||
|
*
|
||||||
|
* Types of session limit which can be imposed on an account. Additional types
|
||||||
|
* may be added in future.
|
||||||
|
*
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
/* these values are used in the com.endlessm.ParentalControls.SessionLimits
|
||||||
|
* D-Bus interface, so must not be changed */
|
||||||
|
MCT_SESSION_LIMITS_TYPE_NONE = 0,
|
||||||
|
MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE = 1,
|
||||||
|
} MctSessionLimitsType;
|
||||||
|
|
||||||
|
struct _MctSessionLimits
|
||||||
|
{
|
||||||
|
gint ref_count;
|
||||||
|
|
||||||
|
uid_t user_id;
|
||||||
|
|
||||||
|
MctSessionLimitsType limit_type;
|
||||||
|
guint daily_start_time; /* seconds since midnight */
|
||||||
|
guint daily_end_time; /* seconds since midnight */
|
||||||
|
};
|
||||||
|
|
||||||
|
G_END_DECLS
|
|
@ -0,0 +1,191 @@
|
||||||
|
/* -*- 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/session-limits.h>
|
||||||
|
|
||||||
|
#include "libmalcontent/session-limits-private.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* struct _MctSessionLimits is defined in session-limits-private.h */
|
||||||
|
|
||||||
|
G_DEFINE_BOXED_TYPE (MctSessionLimits, mct_session_limits,
|
||||||
|
mct_session_limits_ref, mct_session_limits_unref)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_session_limits_ref:
|
||||||
|
* @limits: (transfer none): an #MctSessionLimits
|
||||||
|
*
|
||||||
|
* Increment the reference count of @limits, and return the same pointer to it.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the same pointer as @limits
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
MctSessionLimits *
|
||||||
|
mct_session_limits_ref (MctSessionLimits *limits)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (limits != NULL, NULL);
|
||||||
|
g_return_val_if_fail (limits->ref_count >= 1, NULL);
|
||||||
|
g_return_val_if_fail (limits->ref_count <= G_MAXINT - 1, NULL);
|
||||||
|
|
||||||
|
limits->ref_count++;
|
||||||
|
return limits;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_session_limits_unref:
|
||||||
|
* @limits: (transfer full): an #MctSessionLimits
|
||||||
|
*
|
||||||
|
* Decrement the reference count of @limits. If the reference count reaches
|
||||||
|
* zero, free the @limits and all its resources.
|
||||||
|
*
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
mct_session_limits_unref (MctSessionLimits *limits)
|
||||||
|
{
|
||||||
|
g_return_if_fail (limits != NULL);
|
||||||
|
g_return_if_fail (limits->ref_count >= 1);
|
||||||
|
|
||||||
|
limits->ref_count--;
|
||||||
|
|
||||||
|
if (limits->ref_count <= 0)
|
||||||
|
{
|
||||||
|
g_free (limits);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_session_limits_get_user_id:
|
||||||
|
* @limits: an #MctSessionLimits
|
||||||
|
*
|
||||||
|
* Get the user ID of the user this #MctSessionLimits is for.
|
||||||
|
*
|
||||||
|
* Returns: user ID of the relevant user
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
uid_t
|
||||||
|
mct_session_limits_get_user_id (MctSessionLimits *limits)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (limits != NULL, (uid_t) -1);
|
||||||
|
g_return_val_if_fail (limits->ref_count >= 1, (uid_t) -1);
|
||||||
|
|
||||||
|
return limits->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mct_session_limits_check_time_remaining:
|
||||||
|
* @limits: an #MctSessionLimits
|
||||||
|
* @now_usecs: current time as microseconds since the Unix epoch (UTC),
|
||||||
|
* typically queried using g_get_real_time()
|
||||||
|
* @time_remaining_secs_out: (out) (optional): return location for the number
|
||||||
|
* of seconds remaining before the user’s session has to end, if limits are
|
||||||
|
* in force
|
||||||
|
* @time_limit_enabled_out: (out) (optional): return location for whether time
|
||||||
|
* limits are enabled for this user
|
||||||
|
*
|
||||||
|
* Check whether the user has time remaining in which they are allowed to use
|
||||||
|
* the computer, assuming that @now_usecs is the current time, and applying the
|
||||||
|
* session limit policy from @limits to it.
|
||||||
|
*
|
||||||
|
* This will return whether the user is allowed to use the computer now; further
|
||||||
|
* information about the policy and remaining time is provided in
|
||||||
|
* @time_remaining_secs_out and @time_limit_enabled_out.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the user this @limits corresponds to is allowed to be in
|
||||||
|
* an active session at the given time; %FALSE otherwise
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
mct_session_limits_check_time_remaining (MctSessionLimits *limits,
|
||||||
|
guint64 now_usecs,
|
||||||
|
guint64 *time_remaining_secs_out,
|
||||||
|
gboolean *time_limit_enabled_out)
|
||||||
|
{
|
||||||
|
guint64 time_remaining_secs;
|
||||||
|
gboolean time_limit_enabled;
|
||||||
|
gboolean user_allowed_now;
|
||||||
|
g_autoptr(GDateTime) now_dt = NULL;
|
||||||
|
guint64 now_time_of_day_secs;
|
||||||
|
|
||||||
|
g_return_val_if_fail (limits != NULL, FALSE);
|
||||||
|
g_return_val_if_fail (limits->ref_count >= 1, FALSE);
|
||||||
|
|
||||||
|
/* Helper calculations. */
|
||||||
|
now_dt = g_date_time_new_from_unix_utc (now_usecs / G_USEC_PER_SEC);
|
||||||
|
if (now_dt == NULL)
|
||||||
|
{
|
||||||
|
time_remaining_secs = 0;
|
||||||
|
time_limit_enabled = TRUE;
|
||||||
|
user_allowed_now = FALSE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
now_time_of_day_secs = ((g_date_time_get_hour (now_dt) * 60 +
|
||||||
|
g_date_time_get_minute (now_dt)) * 60 +
|
||||||
|
g_date_time_get_second (now_dt));
|
||||||
|
|
||||||
|
/* Work out the limits. */
|
||||||
|
switch (limits->limit_type)
|
||||||
|
{
|
||||||
|
case MCT_SESSION_LIMITS_TYPE_DAILY_SCHEDULE:
|
||||||
|
user_allowed_now = (now_time_of_day_secs >= limits->daily_start_time &&
|
||||||
|
now_time_of_day_secs < limits->daily_end_time);
|
||||||
|
time_remaining_secs = user_allowed_now ? (limits->daily_end_time - now_time_of_day_secs) : 0;
|
||||||
|
time_limit_enabled = TRUE;
|
||||||
|
|
||||||
|
g_debug ("%s: Daily schedule limit allowed in %u–%u (now is %"
|
||||||
|
G_GUINT64_FORMAT "); %" G_GUINT64_FORMAT " seconds remaining",
|
||||||
|
G_STRFUNC, limits->daily_start_time, limits->daily_end_time,
|
||||||
|
now_time_of_day_secs, time_remaining_secs);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case MCT_SESSION_LIMITS_TYPE_NONE:
|
||||||
|
default:
|
||||||
|
user_allowed_now = TRUE;
|
||||||
|
time_remaining_secs = G_MAXUINT64;
|
||||||
|
time_limit_enabled = FALSE;
|
||||||
|
|
||||||
|
g_debug ("%s: No limit enabled", G_STRFUNC);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
/* Postconditions. */
|
||||||
|
g_assert (!user_allowed_now || time_remaining_secs > 0);
|
||||||
|
g_assert (user_allowed_now || time_remaining_secs == 0);
|
||||||
|
g_assert (time_limit_enabled || time_remaining_secs == G_MAXUINT64);
|
||||||
|
|
||||||
|
/* Output. */
|
||||||
|
if (time_remaining_secs_out != NULL)
|
||||||
|
*time_remaining_secs_out = time_remaining_secs;
|
||||||
|
if (time_limit_enabled_out != NULL)
|
||||||
|
*time_limit_enabled_out = time_limit_enabled;
|
||||||
|
|
||||||
|
return user_allowed_now;
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/* -*- 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>
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MctSessionLimits:
|
||||||
|
*
|
||||||
|
* #MctSessionLimits is an opaque, immutable structure which contains a snapshot
|
||||||
|
* of the session limits settings for a user at a given time. This includes
|
||||||
|
* whether session limits are being enforced, and the limit policy — for
|
||||||
|
* example, the times of day when a user is allowed to use the computer.
|
||||||
|
*
|
||||||
|
* Typically, session limits settings can only be changed by the administrator,
|
||||||
|
* and are read-only for non-administrative users. The precise policy is set
|
||||||
|
* using polkit.
|
||||||
|
*
|
||||||
|
* Since: 0.5.0
|
||||||
|
*/
|
||||||
|
typedef struct _MctSessionLimits MctSessionLimits;
|
||||||
|
GType mct_session_limits_get_type (void);
|
||||||
|
|
||||||
|
MctSessionLimits *mct_session_limits_ref (MctSessionLimits *limits);
|
||||||
|
void mct_session_limits_unref (MctSessionLimits *limits);
|
||||||
|
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MctSessionLimits, mct_session_limits_unref)
|
||||||
|
|
||||||
|
uid_t mct_session_limits_get_user_id (MctSessionLimits *limits);
|
||||||
|
gboolean mct_session_limits_check_time_remaining (MctSessionLimits *limits,
|
||||||
|
guint64 now_usecs,
|
||||||
|
guint64 *time_remaining_secs_out,
|
||||||
|
gboolean *time_limit_enabled_out);
|
||||||
|
|
||||||
|
G_END_DECLS
|
Loading…
Reference in New Issue