libeos-parental-controls: Support disallowing app installation entirely

This is a boolean preference which overrides the OARS values entirely if
FALSE.

This change breaks ABI for EpcAppFilterBuilder, but since that hasn’t
been used in any code we’ve shipped yet, that should be OK.

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

https://phabricator.endlessm.com/T24457
This commit is contained in:
Philip Withnall 2018-11-13 11:30:19 +00:00
parent 87867f072d
commit 60a938de0e
4 changed files with 117 additions and 1 deletions

View File

@ -59,5 +59,18 @@
<annotation name="org.freedesktop.Accounts.DefaultValue" <annotation name="org.freedesktop.Accounts.DefaultValue"
value="('oars-1.1', @a{ss} {})"/> value="('oars-1.1', @a{ss} {})"/>
</property> </property>
<!--
allow-app-installation:
Whether app installation is allowed for the user at all. If this is true,
the polkit check for allowing app installation succeeds, and the
oars-filter does not restrict this app, app installation can proceed.
If this is false, the user is not allowed to install any apps.
-->
<property name="allow-app-installation" type="b" access="readwrite">
<annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
</property>
</interface> </interface>
</node> </node>

View File

@ -58,6 +58,7 @@ struct _EpcAppFilter
EpcAppFilterListType app_list_type; EpcAppFilterListType app_list_type;
GVariant *oars_ratings; /* (type a{ss}) (owned non-floating) */ GVariant *oars_ratings; /* (type a{ss}) (owned non-floating) */
gboolean allow_app_installation;
}; };
G_DEFINE_BOXED_TYPE (EpcAppFilter, epc_app_filter, G_DEFINE_BOXED_TYPE (EpcAppFilter, epc_app_filter,
@ -254,6 +255,8 @@ epc_app_filter_get_oars_sections (EpcAppFilter *filter)
* section, inclusive. Any app with a more intense value for this section must * section, inclusive. Any app with a more intense value for this section must
* be hidden from the user whose @filter this is. * be hidden from the user whose @filter this is.
* *
* This does not factor in epc_app_filter_is_app_installation_allowed().
*
* Returns: an #EpcAppFilterOarsValue * Returns: an #EpcAppFilterOarsValue
* Since: 0.1.0 * Since: 0.1.0
*/ */
@ -284,6 +287,28 @@ epc_app_filter_get_oars_value (EpcAppFilter *filter,
return EPC_APP_FILTER_OARS_VALUE_UNKNOWN; return EPC_APP_FILTER_OARS_VALUE_UNKNOWN;
} }
/**
* epc_app_filter_is_app_installation_allowed:
* @filter: an #EpcAppFilter
*
* Get whether app installation is allowed at all for the user. This should be
* queried in addition to the OARS values (epc_app_filter_get_oars_value()) if
* it returns %FALSE, the OARS values should be ignored and app installation
* should be unconditionally disallowed.
*
* Returns: %TRUE if app installation is allowed in general for this user;
* %FALSE if it is unconditionally disallowed for this user
* Since: 0.1.0
*/
gboolean
epc_app_filter_is_app_installation_allowed (EpcAppFilter *filter)
{
g_return_val_if_fail (filter != NULL, FALSE);
g_return_val_if_fail (filter->ref_count >= 1, FALSE);
return filter->allow_app_installation;
}
/** /**
* _epc_app_filter_build_app_filter_variant: * _epc_app_filter_build_app_filter_variant:
* @filter: an #EpcAppFilter * @filter: an #EpcAppFilter
@ -420,6 +445,7 @@ epc_get_app_filter (GDBusConnection *connection,
const gchar *content_rating_kind; const gchar *content_rating_kind;
g_autoptr(GVariant) oars_variant = NULL; g_autoptr(GVariant) oars_variant = NULL;
g_autoptr(GHashTable) oars_map = NULL; g_autoptr(GHashTable) oars_map = NULL;
gboolean allow_app_installation;
g_return_val_if_fail (connection == NULL || G_IS_DBUS_CONNECTION (connection), NULL); 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 (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
@ -491,6 +517,13 @@ epc_get_app_filter (GDBusConnection *connection,
return NULL; return NULL;
} }
if (!g_variant_lookup (properties, "allow-app-installation", "b",
&allow_app_installation))
{
/* Default value. */
allow_app_installation = TRUE;
}
/* Success. Create an #EpcAppFilter object to contain the results. */ /* Success. Create an #EpcAppFilter object to contain the results. */
app_filter = g_new0 (EpcAppFilter, 1); app_filter = g_new0 (EpcAppFilter, 1);
app_filter->ref_count = 1; app_filter->ref_count = 1;
@ -499,6 +532,7 @@ epc_get_app_filter (GDBusConnection *connection,
app_filter->app_list_type = app_filter->app_list_type =
is_whitelist ? EPC_APP_FILTER_LIST_WHITELIST : EPC_APP_FILTER_LIST_BLACKLIST; is_whitelist ? EPC_APP_FILTER_LIST_WHITELIST : EPC_APP_FILTER_LIST_BLACKLIST;
app_filter->oars_ratings = g_steal_pointer (&oars_variant); app_filter->oars_ratings = g_steal_pointer (&oars_variant);
app_filter->allow_app_installation = allow_app_installation;
return g_steal_pointer (&app_filter); return g_steal_pointer (&app_filter);
} }
@ -643,8 +677,10 @@ epc_set_app_filter (GDBusConnection *connection,
g_autofree gchar *object_path = NULL; g_autofree gchar *object_path = NULL;
g_autoptr(GVariant) app_filter_variant = NULL; g_autoptr(GVariant) app_filter_variant = NULL;
g_autoptr(GVariant) oars_filter_variant = NULL; g_autoptr(GVariant) oars_filter_variant = NULL;
g_autoptr(GVariant) allow_app_installation_variant = NULL;
g_autoptr(GVariant) app_filter_result_variant = NULL; g_autoptr(GVariant) app_filter_result_variant = NULL;
g_autoptr(GVariant) oars_filter_result_variant = NULL; g_autoptr(GVariant) oars_filter_result_variant = NULL;
g_autoptr(GVariant) allow_app_installation_result_variant = NULL;
g_autoptr(GError) local_error = 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 (connection == NULL || G_IS_DBUS_CONNECTION (connection), FALSE);
@ -667,6 +703,7 @@ epc_set_app_filter (GDBusConnection *connection,
app_filter_variant = _epc_app_filter_build_app_filter_variant (app_filter); app_filter_variant = _epc_app_filter_build_app_filter_variant (app_filter);
oars_filter_variant = g_variant_new ("(s@a{ss})", "oars-1.1", oars_filter_variant = g_variant_new ("(s@a{ss})", "oars-1.1",
app_filter->oars_ratings); app_filter->oars_ratings);
allow_app_installation_variant = g_variant_new_boolean (app_filter->allow_app_installation);
app_filter_result_variant = app_filter_result_variant =
g_dbus_connection_call_sync (connection, g_dbus_connection_call_sync (connection,
@ -714,6 +751,29 @@ epc_set_app_filter (GDBusConnection *connection,
return FALSE; return FALSE;
} }
allow_app_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",
"allow-app-installation",
g_steal_pointer (&allow_app_installation_variant)),
G_VARIANT_TYPE ("()"),
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_propagate_error (error, bus_error_to_app_filter_error (local_error, user_id));
return FALSE;
}
return TRUE; return TRUE;
} }
@ -845,6 +905,7 @@ typedef struct
{ {
GPtrArray *paths_blacklist; /* (nullable) (owned) (element-type filename) */ GPtrArray *paths_blacklist; /* (nullable) (owned) (element-type filename) */
GHashTable *oars; /* (nullable) (owned) (element-type utf8 EpcAppFilterOarsValue) */ GHashTable *oars; /* (nullable) (owned) (element-type utf8 EpcAppFilterOarsValue) */
gboolean allow_app_installation;
/*< private >*/ /*< private >*/
gpointer padding[2]; gpointer padding[2];
@ -959,6 +1020,7 @@ epc_app_filter_builder_copy (EpcAppFilterBuilder *builder)
_copy->paths_blacklist = g_ptr_array_ref (_builder->paths_blacklist); _copy->paths_blacklist = g_ptr_array_ref (_builder->paths_blacklist);
if (_builder->oars != NULL) if (_builder->oars != NULL)
_copy->oars = g_hash_table_ref (_builder->oars); _copy->oars = g_hash_table_ref (_builder->oars);
_copy->allow_app_installation = _builder->allow_app_installation;
return g_steal_pointer (&copy); return g_steal_pointer (&copy);
} }
@ -1042,6 +1104,7 @@ epc_app_filter_builder_end (EpcAppFilterBuilder *builder)
app_filter->app_list = (gchar **) g_ptr_array_free (g_steal_pointer (&_builder->paths_blacklist), FALSE); app_filter->app_list = (gchar **) g_ptr_array_free (g_steal_pointer (&_builder->paths_blacklist), FALSE);
app_filter->app_list_type = EPC_APP_FILTER_LIST_BLACKLIST; app_filter->app_list_type = EPC_APP_FILTER_LIST_BLACKLIST;
app_filter->oars_ratings = g_steal_pointer (&oars_variant); app_filter->oars_ratings = g_steal_pointer (&oars_variant);
app_filter->allow_app_installation = _builder->allow_app_installation;
epc_app_filter_builder_clear (builder); epc_app_filter_builder_clear (builder);
@ -1130,3 +1193,27 @@ epc_app_filter_builder_set_oars_value (EpcAppFilterBuilder *builder,
g_hash_table_insert (_builder->oars, g_strdup (oars_section), g_hash_table_insert (_builder->oars, g_strdup (oars_section),
GUINT_TO_POINTER (value)); GUINT_TO_POINTER (value));
} }
/**
* epc_app_filter_builder_set_allow_app_installation:
* @builder: an initialised #EpcAppFilterBuilder
* @allow_app_installation: %TRUE to allow app installation; %FALSE to
* unconditionally disallow it
*
* Set whether app installation is allowed in general for the user. If this is
* %TRUE, app installation is still subject to the OARS values
* (epc_app_filter_builder_set_oars_value()). If it is %FALSE, app installation
* is unconditionally disallowed for this user.
*
* Since: 0.1.0
*/
void
epc_app_filter_builder_set_allow_app_installation (EpcAppFilterBuilder *builder,
gboolean allow_app_installation)
{
EpcAppFilterBuilderReal *_builder = (EpcAppFilterBuilderReal *) builder;
g_return_if_fail (_builder != NULL);
_builder->allow_app_installation = allow_app_installation;
}

View File

@ -107,6 +107,8 @@ const gchar **epc_app_filter_get_oars_sections (EpcAppFilter *filter);
EpcAppFilterOarsValue epc_app_filter_get_oars_value (EpcAppFilter *filter, EpcAppFilterOarsValue epc_app_filter_get_oars_value (EpcAppFilter *filter,
const gchar *oars_section); const gchar *oars_section);
gboolean epc_app_filter_is_app_installation_allowed (EpcAppFilter *filter);
EpcAppFilter *epc_get_app_filter (GDBusConnection *connection, EpcAppFilter *epc_get_app_filter (GDBusConnection *connection,
uid_t user_id, uid_t user_id,
gboolean allow_interactive_authorization, gboolean allow_interactive_authorization,
@ -152,6 +154,7 @@ typedef struct
/*< private >*/ /*< private >*/
gpointer p0; gpointer p0;
gpointer p1; gpointer p1;
gboolean b0;
gpointer p2; gpointer p2;
gpointer p3; gpointer p3;
} EpcAppFilterBuilder; } EpcAppFilterBuilder;
@ -174,7 +177,8 @@ GType epc_app_filter_builder_get_type (void);
#define EPC_APP_FILTER_BUILDER_INIT() \ #define EPC_APP_FILTER_BUILDER_INIT() \
{ \ { \
g_ptr_array_new_with_free_func (g_free), \ g_ptr_array_new_with_free_func (g_free), \
g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL) \ g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL), \
TRUE, \
} }
void epc_app_filter_builder_init (EpcAppFilterBuilder *builder); void epc_app_filter_builder_init (EpcAppFilterBuilder *builder);
@ -199,4 +203,7 @@ void epc_app_filter_builder_set_oars_value (EpcAppFilterBuilder *builde
const gchar *oars_section, const gchar *oars_section,
EpcAppFilterOarsValue value); EpcAppFilterOarsValue value);
void epc_app_filter_builder_set_allow_app_installation (EpcAppFilterBuilder *builder,
gboolean allow_app_installation);
G_END_DECLS G_END_DECLS

View File

@ -125,6 +125,7 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture,
EPC_APP_FILTER_OARS_VALUE_MILD); EPC_APP_FILTER_OARS_VALUE_MILD);
epc_app_filter_builder_set_oars_value (fixture->builder, "language-humor", epc_app_filter_builder_set_oars_value (fixture->builder, "language-humor",
EPC_APP_FILTER_OARS_VALUE_MODERATE); EPC_APP_FILTER_OARS_VALUE_MODERATE);
epc_app_filter_builder_set_allow_app_installation (fixture->builder, FALSE);
filter = epc_app_filter_builder_end (fixture->builder); filter = epc_app_filter_builder_end (fixture->builder);
@ -147,6 +148,8 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture,
sections = epc_app_filter_get_oars_sections (filter); sections = epc_app_filter_get_oars_sections (filter);
const gchar * const expected_sections[] = { "drugs-alcohol", "language-humor", NULL }; const gchar * const expected_sections[] = { "drugs-alcohol", "language-humor", NULL };
assert_strv_equal ((const gchar * const *) sections, expected_sections); assert_strv_equal ((const gchar * const *) sections, expected_sections);
g_assert_false (epc_app_filter_is_app_installation_allowed (filter));
} }
/* Test building an empty #EpcAppFilter using an #EpcAppFilterBuilder. */ /* Test building an empty #EpcAppFilter using an #EpcAppFilterBuilder. */
@ -178,6 +181,8 @@ test_app_filter_builder_empty (BuilderFixture *fixture,
sections = epc_app_filter_get_oars_sections (filter); sections = epc_app_filter_get_oars_sections (filter);
const gchar * const expected_sections[] = { NULL }; const gchar * const expected_sections[] = { NULL };
assert_strv_equal ((const gchar * const *) sections, expected_sections); assert_strv_equal ((const gchar * const *) sections, expected_sections);
g_assert_true (epc_app_filter_is_app_installation_allowed (filter));
} }
/* Check that copying a cleared #EpcAppFilterBuilder works, and the copy can /* Check that copying a cleared #EpcAppFilterBuilder works, and the copy can
@ -198,6 +203,8 @@ test_app_filter_builder_copy_empty (void)
g_assert_true (epc_app_filter_is_path_allowed (filter, "/bin/false")); g_assert_true (epc_app_filter_is_path_allowed (filter, "/bin/false"));
g_assert_false (epc_app_filter_is_path_allowed (filter, "/bin/true")); g_assert_false (epc_app_filter_is_path_allowed (filter, "/bin/true"));
g_assert_true (epc_app_filter_is_app_installation_allowed (filter));
} }
/* Check that copying a filled #EpcAppFilterBuilder works, and the copy can be /* Check that copying a filled #EpcAppFilterBuilder works, and the copy can be
@ -210,11 +217,13 @@ test_app_filter_builder_copy_full (void)
g_autoptr(EpcAppFilter) filter = NULL; g_autoptr(EpcAppFilter) filter = NULL;
epc_app_filter_builder_blacklist_path (builder, "/bin/true"); epc_app_filter_builder_blacklist_path (builder, "/bin/true");
epc_app_filter_builder_set_allow_app_installation (builder, FALSE);
builder_copy = epc_app_filter_builder_copy (builder); builder_copy = epc_app_filter_builder_copy (builder);
filter = epc_app_filter_builder_end (builder_copy); filter = epc_app_filter_builder_end (builder_copy);
g_assert_true (epc_app_filter_is_path_allowed (filter, "/bin/false")); g_assert_true (epc_app_filter_is_path_allowed (filter, "/bin/false"));
g_assert_false (epc_app_filter_is_path_allowed (filter, "/bin/true")); g_assert_false (epc_app_filter_is_path_allowed (filter, "/bin/true"));
g_assert_false (epc_app_filter_is_app_installation_allowed (filter));
} }
int int