From 60a938de0eedcf6ae2ce85ad17a82370e6e46791 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 13 Nov 2018 11:30:19 +0000 Subject: [PATCH 1/3] libeos-parental-controls: Support disallowing app installation entirely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 https://phabricator.endlessm.com/T24457 --- ...om.endlessm.ParentalControls.AppFilter.xml | 13 +++ libeos-parental-controls/app-filter.c | 87 +++++++++++++++++++ libeos-parental-controls/app-filter.h | 9 +- libeos-parental-controls/tests/app-filter.c | 9 ++ 4 files changed, 117 insertions(+), 1 deletion(-) diff --git a/accounts-service/com.endlessm.ParentalControls.AppFilter.xml b/accounts-service/com.endlessm.ParentalControls.AppFilter.xml index af73110..8d4d554 100644 --- a/accounts-service/com.endlessm.ParentalControls.AppFilter.xml +++ b/accounts-service/com.endlessm.ParentalControls.AppFilter.xml @@ -59,5 +59,18 @@ + + + + + diff --git a/libeos-parental-controls/app-filter.c b/libeos-parental-controls/app-filter.c index d462d8f..8d8e085 100644 --- a/libeos-parental-controls/app-filter.c +++ b/libeos-parental-controls/app-filter.c @@ -58,6 +58,7 @@ struct _EpcAppFilter EpcAppFilterListType app_list_type; GVariant *oars_ratings; /* (type a{ss}) (owned non-floating) */ + gboolean allow_app_installation; }; 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 * be hidden from the user whose @filter this is. * + * This does not factor in epc_app_filter_is_app_installation_allowed(). + * * Returns: an #EpcAppFilterOarsValue * Since: 0.1.0 */ @@ -284,6 +287,28 @@ epc_app_filter_get_oars_value (EpcAppFilter *filter, 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: * @filter: an #EpcAppFilter @@ -420,6 +445,7 @@ epc_get_app_filter (GDBusConnection *connection, const gchar *content_rating_kind; g_autoptr(GVariant) oars_variant = 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 (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL); @@ -491,6 +517,13 @@ epc_get_app_filter (GDBusConnection *connection, 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. */ app_filter = g_new0 (EpcAppFilter, 1); app_filter->ref_count = 1; @@ -499,6 +532,7 @@ epc_get_app_filter (GDBusConnection *connection, app_filter->app_list_type = is_whitelist ? EPC_APP_FILTER_LIST_WHITELIST : EPC_APP_FILTER_LIST_BLACKLIST; app_filter->oars_ratings = g_steal_pointer (&oars_variant); + app_filter->allow_app_installation = allow_app_installation; return g_steal_pointer (&app_filter); } @@ -643,8 +677,10 @@ epc_set_app_filter (GDBusConnection *connection, g_autofree gchar *object_path = NULL; g_autoptr(GVariant) app_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) oars_filter_result_variant = NULL; + g_autoptr(GVariant) allow_app_installation_result_variant = NULL; g_autoptr(GError) local_error = NULL; 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); oars_filter_variant = g_variant_new ("(s@a{ss})", "oars-1.1", app_filter->oars_ratings); + allow_app_installation_variant = g_variant_new_boolean (app_filter->allow_app_installation); app_filter_result_variant = g_dbus_connection_call_sync (connection, @@ -714,6 +751,29 @@ epc_set_app_filter (GDBusConnection *connection, 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; } @@ -845,6 +905,7 @@ typedef struct { GPtrArray *paths_blacklist; /* (nullable) (owned) (element-type filename) */ GHashTable *oars; /* (nullable) (owned) (element-type utf8 EpcAppFilterOarsValue) */ + gboolean allow_app_installation; /*< private >*/ gpointer padding[2]; @@ -959,6 +1020,7 @@ epc_app_filter_builder_copy (EpcAppFilterBuilder *builder) _copy->paths_blacklist = g_ptr_array_ref (_builder->paths_blacklist); if (_builder->oars != NULL) _copy->oars = g_hash_table_ref (_builder->oars); + _copy->allow_app_installation = _builder->allow_app_installation; return g_steal_pointer (©); } @@ -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_type = EPC_APP_FILTER_LIST_BLACKLIST; app_filter->oars_ratings = g_steal_pointer (&oars_variant); + app_filter->allow_app_installation = _builder->allow_app_installation; 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), 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; +} diff --git a/libeos-parental-controls/app-filter.h b/libeos-parental-controls/app-filter.h index 5abb28a..ab6280a 100644 --- a/libeos-parental-controls/app-filter.h +++ b/libeos-parental-controls/app-filter.h @@ -107,6 +107,8 @@ const gchar **epc_app_filter_get_oars_sections (EpcAppFilter *filter); EpcAppFilterOarsValue epc_app_filter_get_oars_value (EpcAppFilter *filter, const gchar *oars_section); +gboolean epc_app_filter_is_app_installation_allowed (EpcAppFilter *filter); + EpcAppFilter *epc_get_app_filter (GDBusConnection *connection, uid_t user_id, gboolean allow_interactive_authorization, @@ -152,6 +154,7 @@ typedef struct /*< private >*/ gpointer p0; gpointer p1; + gboolean b0; gpointer p2; gpointer p3; } EpcAppFilterBuilder; @@ -174,7 +177,8 @@ GType epc_app_filter_builder_get_type (void); #define EPC_APP_FILTER_BUILDER_INIT() \ { \ 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); @@ -199,4 +203,7 @@ void epc_app_filter_builder_set_oars_value (EpcAppFilterBuilder *builde const gchar *oars_section, EpcAppFilterOarsValue value); +void epc_app_filter_builder_set_allow_app_installation (EpcAppFilterBuilder *builder, + gboolean allow_app_installation); + G_END_DECLS diff --git a/libeos-parental-controls/tests/app-filter.c b/libeos-parental-controls/tests/app-filter.c index 6e2eadc..2bbbb63 100644 --- a/libeos-parental-controls/tests/app-filter.c +++ b/libeos-parental-controls/tests/app-filter.c @@ -125,6 +125,7 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture, EPC_APP_FILTER_OARS_VALUE_MILD); epc_app_filter_builder_set_oars_value (fixture->builder, "language-humor", 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); @@ -147,6 +148,8 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture, sections = epc_app_filter_get_oars_sections (filter); const gchar * const expected_sections[] = { "drugs-alcohol", "language-humor", NULL }; 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. */ @@ -178,6 +181,8 @@ test_app_filter_builder_empty (BuilderFixture *fixture, sections = epc_app_filter_get_oars_sections (filter); const gchar * const expected_sections[] = { NULL }; 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 @@ -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_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 @@ -210,11 +217,13 @@ test_app_filter_builder_copy_full (void) g_autoptr(EpcAppFilter) filter = NULL; 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); filter = epc_app_filter_builder_end (builder_copy); 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_app_installation_allowed (filter)); } int From 94beacb4e3d3cb48fea3fe62ab6e1bdfd623e54e Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 13 Nov 2018 11:45:41 +0000 Subject: [PATCH 2/3] eos-parental-controls-client: Support setting allow-app-installation key Signed-off-by: Philip Withnall https://phabricator.endlessm.com/T24457 --- .../eos-parental-controls-client.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/eos-parental-controls-client/eos-parental-controls-client.py b/eos-parental-controls-client/eos-parental-controls-client.py index e876276..f803dc5 100644 --- a/eos-parental-controls-client/eos-parental-controls-client.py +++ b/eos-parental-controls-client/eos-parental-controls-client.py @@ -169,10 +169,12 @@ def command_oars_section(user, section, quiet=False, interactive=True): section, user_id, __oars_value_to_string(value))) -def command_set(user, app_filter_args=None, quiet=False, interactive=True): +def command_set(user, allow_app_installation=True, app_filter_args=None, + quiet=False, interactive=True): """Set the app filter for the given user.""" user_id = __lookup_user_id_or_error(user) builder = EosParentalControls.AppFilterBuilder.new() + builder.set_allow_app_installation(allow_app_installation) for arg in app_filter_args: if '=' in arg: @@ -258,9 +260,17 @@ def main(): parser_set.add_argument('user', default='', nargs='?', help='user ID or username to get the app filter ' 'for (default: current user)') + parser_set.add_argument('--allow-app-installation', + dest='allow_app_installation', action='store_true', + help='allow app installation in general') + parser_set.add_argument('--disallow-app-installation', + dest='allow_app_installation', + action='store_false', + help='unconditionally disallow app installation') parser_set.add_argument('app_filter_args', nargs='*', help='paths to blacklist and OARS section=value ' 'pairs to store') + parser_set.set_defaults(allow_app_installation=True) # Parse the command line arguments and run the subcommand. args = parser.parse_args() From 9a890750c4b431d7da761980d4ac8562dc2d0885 Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Tue, 13 Nov 2018 11:46:24 +0000 Subject: [PATCH 3/3] =?UTF-8?q?eos-parental-controls-client:=20Print=20app?= =?UTF-8?q?=20filter=20on=20=E2=80=98get=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously we just printed whether the filter was successfully retrieved, since there weren’t any non-parameterised getter functions on EpcAppFilter. Now we have some of them, we can print details of the app filter. Signed-off-by: Philip Withnall https://phabricator.endlessm.com/T24457 --- .../eos-parental-controls-client.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/eos-parental-controls-client/eos-parental-controls-client.py b/eos-parental-controls-client/eos-parental-controls-client.py index f803dc5..24c4b73 100644 --- a/eos-parental-controls-client/eos-parental-controls-client.py +++ b/eos-parental-controls-client/eos-parental-controls-client.py @@ -127,9 +127,21 @@ def __oars_value_from_string(value_str): def command_get(user, quiet=False, interactive=True): """Get the app filter for the given user.""" user_id = __lookup_user_id_or_error(user) - __get_app_filter_or_error(user_id, interactive) + app_filter = __get_app_filter_or_error(user_id, interactive) - print('App filter for user {} retrieved'.format(user_id)) + print('App filter for user {} retrieved:'.format(user_id)) + + sections = app_filter.get_oars_sections() + for section in sections: + value = app_filter.get_oars_value(section) + print(' {}: {}'.format(section, oars_value_mapping[value])) + if not sections: + print(' (No OARS values)') + + if app_filter.is_app_installation_allowed(): + print('App installation is allowed') + else: + print('App installation is disallowed') def command_check(user, path, quiet=False, interactive=True):