Merge branch 'filter-content-type' into 'master'
Add support to filter by content type See merge request pwithnall/malcontent!15
This commit is contained in:
commit
90f961dc74
|
@ -1,6 +1,6 @@
|
|||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright © 2018 Endless Mobile, Inc.
|
||||
* 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
|
||||
|
@ -18,6 +18,7 @@
|
|||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
* - Andre Moreira Magalhaes <andre@endlessm.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
@ -124,8 +125,12 @@ mct_app_filter_is_path_allowed (MctAppFilter *filter,
|
|||
g_return_val_if_fail (g_path_is_absolute (path), FALSE);
|
||||
|
||||
g_autofree gchar *canonical_path = g_canonicalize_filename (path, "/");
|
||||
g_autofree gchar *canonical_path_utf8 = g_filename_to_utf8 (canonical_path, -1,
|
||||
NULL, NULL, NULL);
|
||||
g_return_val_if_fail (canonical_path_utf8 != NULL, FALSE);
|
||||
|
||||
gboolean path_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
|
||||
canonical_path);
|
||||
canonical_path_utf8);
|
||||
|
||||
switch (filter->app_list_type)
|
||||
{
|
||||
|
@ -138,6 +143,34 @@ mct_app_filter_is_path_allowed (MctAppFilter *filter,
|
|||
}
|
||||
}
|
||||
|
||||
/* Check whether a given @ref is a valid flatpak ref.
|
||||
*
|
||||
* For simplicity and to avoid duplicating the whole logic behind
|
||||
* flatpak_ref_parse() this method will only check whether:
|
||||
* - the @ref contains exactly 3 slash chars
|
||||
* - the @ref starts with either app/ or runtime/
|
||||
* - the name, arch and branch components of the @ref are not empty
|
||||
*
|
||||
* We avoid using flatpak_ref_parse() to allow for libflatpak
|
||||
* to depend on malcontent without causing a cyclic dependency.
|
||||
*/
|
||||
static gboolean
|
||||
is_valid_flatpak_ref (const gchar *ref)
|
||||
{
|
||||
g_auto(GStrv) parts = NULL;
|
||||
|
||||
if (ref == NULL)
|
||||
return FALSE;
|
||||
|
||||
parts = g_strsplit (ref, "/", 0);
|
||||
return (g_strv_length (parts) == 4 &&
|
||||
(strcmp (parts[0], "app") == 0 ||
|
||||
strcmp (parts[0], "runtime") == 0) &&
|
||||
*parts[1] != '\0' &&
|
||||
*parts[2] != '\0' &&
|
||||
*parts[3] != '\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* mct_app_filter_is_flatpak_ref_allowed:
|
||||
* @filter: an #MctAppFilter
|
||||
|
@ -157,6 +190,7 @@ mct_app_filter_is_flatpak_ref_allowed (MctAppFilter *filter,
|
|||
g_return_val_if_fail (filter != NULL, FALSE);
|
||||
g_return_val_if_fail (filter->ref_count >= 1, FALSE);
|
||||
g_return_val_if_fail (app_ref != NULL, FALSE);
|
||||
g_return_val_if_fail (is_valid_flatpak_ref (app_ref), FALSE);
|
||||
|
||||
gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
|
||||
app_ref);
|
||||
|
@ -201,9 +235,8 @@ mct_app_filter_is_flatpak_app_allowed (MctAppFilter *filter,
|
|||
gboolean id_in_list = FALSE;
|
||||
for (gsize i = 0; filter->app_list[i] != NULL; i++)
|
||||
{
|
||||
/* Avoid using flatpak_ref_parse() to avoid a dependency on libflatpak
|
||||
* just for that one function. */
|
||||
if (g_str_has_prefix (filter->app_list[i], "app/") &&
|
||||
if (is_valid_flatpak_ref (filter->app_list[i]) &&
|
||||
g_str_has_prefix (filter->app_list[i], "app/") &&
|
||||
strncmp (filter->app_list[i] + strlen ("app/"), app_id, app_id_len) == 0 &&
|
||||
filter->app_list[i][strlen ("app/") + app_id_len] == '/')
|
||||
{
|
||||
|
@ -242,6 +275,7 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
|
|||
GAppInfo *app_info)
|
||||
{
|
||||
g_autofree gchar *abs_path = NULL;
|
||||
const gchar * const *types = NULL;
|
||||
|
||||
g_return_val_if_fail (filter != NULL, FALSE);
|
||||
g_return_val_if_fail (filter->ref_count >= 1, FALSE);
|
||||
|
@ -253,6 +287,13 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
|
|||
!mct_app_filter_is_path_allowed (filter, abs_path))
|
||||
return FALSE;
|
||||
|
||||
types = g_app_info_get_supported_types (app_info);
|
||||
for (gsize i = 0; types != NULL && types[i] != NULL; i++)
|
||||
{
|
||||
if (!mct_app_filter_is_content_type_allowed (filter, types[i]))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (G_IS_DESKTOP_APP_INFO (app_info))
|
||||
{
|
||||
g_autofree gchar *flatpak_app = NULL;
|
||||
|
@ -292,6 +333,67 @@ mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
/* Check whether a given @content_type is valid.
|
||||
*
|
||||
* For simplicity this method will only check whether:
|
||||
* - the @content_type contains exactly 1 slash char
|
||||
* - the @content_type does not start with a slash char
|
||||
* - the type and subtype components of the @content_type are not empty
|
||||
*/
|
||||
static gboolean
|
||||
is_valid_content_type (const gchar *content_type)
|
||||
{
|
||||
g_auto(GStrv) parts = NULL;
|
||||
|
||||
if (content_type == NULL)
|
||||
return FALSE;
|
||||
|
||||
parts = g_strsplit (content_type, "/", 0);
|
||||
return (g_strv_length (parts) == 2 &&
|
||||
*parts[0] != '\0' &&
|
||||
*parts[1] != '\0');
|
||||
}
|
||||
|
||||
/**
|
||||
* mct_app_filter_is_content_type_allowed:
|
||||
* @filter: an #MctAppFilter
|
||||
* @content_type: content type to check
|
||||
*
|
||||
* Check whether apps handling the given @content_type are allowed to be run
|
||||
* according to this app filter.
|
||||
*
|
||||
* Note that this method doesn’t match content subtypes. For example, if
|
||||
* `application/xml` is added to the blacklist but `application/xspf+xml` is not,
|
||||
* a check for whether `application/xspf+xml` is blacklisted would return false.
|
||||
*
|
||||
* Returns: %TRUE if the user this @filter corresponds to is allowed to run
|
||||
* programs handling @content_type according to the @filter policy;
|
||||
* %FALSE otherwise
|
||||
* Since: 0.4.0
|
||||
*/
|
||||
gboolean
|
||||
mct_app_filter_is_content_type_allowed (MctAppFilter *filter,
|
||||
const gchar *content_type)
|
||||
{
|
||||
g_return_val_if_fail (filter != NULL, FALSE);
|
||||
g_return_val_if_fail (filter->ref_count >= 1, FALSE);
|
||||
g_return_val_if_fail (content_type != NULL, FALSE);
|
||||
g_return_val_if_fail (is_valid_content_type (content_type), FALSE);
|
||||
|
||||
gboolean ref_in_list = g_strv_contains ((const gchar * const *) filter->app_list,
|
||||
content_type);
|
||||
|
||||
switch (filter->app_list_type)
|
||||
{
|
||||
case MCT_APP_FILTER_LIST_BLACKLIST:
|
||||
return !ref_in_list;
|
||||
case MCT_APP_FILTER_LIST_WHITELIST:
|
||||
return ref_in_list;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
static gint
|
||||
strcmp_cb (gconstpointer a,
|
||||
gconstpointer b)
|
||||
|
@ -433,7 +535,7 @@ mct_app_filter_is_system_installation_allowed (MctAppFilter *filter)
|
|||
*/
|
||||
typedef struct
|
||||
{
|
||||
GPtrArray *paths_blacklist; /* (nullable) (owned) (element-type filename) */
|
||||
GPtrArray *blacklist; /* (nullable) (owned) (element-type utf8) */
|
||||
GHashTable *oars; /* (nullable) (owned) (element-type utf8 MctAppFilterOarsValue) */
|
||||
gboolean allow_user_installation;
|
||||
gboolean allow_system_installation;
|
||||
|
@ -471,7 +573,7 @@ mct_app_filter_builder_init (MctAppFilterBuilder *builder)
|
|||
MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
|
||||
|
||||
g_return_if_fail (_builder != NULL);
|
||||
g_return_if_fail (_builder->paths_blacklist == NULL);
|
||||
g_return_if_fail (_builder->blacklist == NULL);
|
||||
g_return_if_fail (_builder->oars == NULL);
|
||||
|
||||
memcpy (builder, &local_builder, sizeof (local_builder));
|
||||
|
@ -497,7 +599,7 @@ mct_app_filter_builder_clear (MctAppFilterBuilder *builder)
|
|||
|
||||
g_return_if_fail (_builder != NULL);
|
||||
|
||||
g_clear_pointer (&_builder->paths_blacklist, g_ptr_array_unref);
|
||||
g_clear_pointer (&_builder->blacklist, g_ptr_array_unref);
|
||||
g_clear_pointer (&_builder->oars, g_hash_table_unref);
|
||||
}
|
||||
|
||||
|
@ -547,8 +649,8 @@ mct_app_filter_builder_copy (MctAppFilterBuilder *builder)
|
|||
_copy = (MctAppFilterBuilderReal *) copy;
|
||||
|
||||
mct_app_filter_builder_clear (copy);
|
||||
if (_builder->paths_blacklist != NULL)
|
||||
_copy->paths_blacklist = g_ptr_array_ref (_builder->paths_blacklist);
|
||||
if (_builder->blacklist != NULL)
|
||||
_copy->blacklist = g_ptr_array_ref (_builder->blacklist);
|
||||
if (_builder->oars != NULL)
|
||||
_copy->oars = g_hash_table_ref (_builder->oars);
|
||||
_copy->allow_user_installation = _builder->allow_user_installation;
|
||||
|
@ -598,11 +700,11 @@ mct_app_filter_builder_end (MctAppFilterBuilder *builder)
|
|||
g_autoptr(GVariant) oars_variant = NULL;
|
||||
|
||||
g_return_val_if_fail (_builder != NULL, NULL);
|
||||
g_return_val_if_fail (_builder->paths_blacklist != NULL, NULL);
|
||||
g_return_val_if_fail (_builder->blacklist != NULL, NULL);
|
||||
g_return_val_if_fail (_builder->oars != NULL, NULL);
|
||||
|
||||
/* Ensure the paths list is %NULL-terminated. */
|
||||
g_ptr_array_add (_builder->paths_blacklist, NULL);
|
||||
g_ptr_array_add (_builder->blacklist, NULL);
|
||||
|
||||
/* Build the OARS variant. */
|
||||
g_hash_table_iter_init (&iter, _builder->oars);
|
||||
|
@ -633,7 +735,7 @@ mct_app_filter_builder_end (MctAppFilterBuilder *builder)
|
|||
app_filter = g_new0 (MctAppFilter, 1);
|
||||
app_filter->ref_count = 1;
|
||||
app_filter->user_id = -1;
|
||||
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->blacklist), FALSE);
|
||||
app_filter->app_list_type = MCT_APP_FILTER_LIST_BLACKLIST;
|
||||
app_filter->oars_ratings = g_steal_pointer (&oars_variant);
|
||||
app_filter->allow_user_installation = _builder->allow_user_installation;
|
||||
|
@ -662,15 +764,18 @@ mct_app_filter_builder_blacklist_path (MctAppFilterBuilder *builder,
|
|||
MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
|
||||
|
||||
g_return_if_fail (_builder != NULL);
|
||||
g_return_if_fail (_builder->paths_blacklist != NULL);
|
||||
g_return_if_fail (_builder->blacklist != NULL);
|
||||
g_return_if_fail (path != NULL);
|
||||
g_return_if_fail (g_path_is_absolute (path));
|
||||
|
||||
g_autofree gchar *canonical_path = g_canonicalize_filename (path, "/");
|
||||
g_autofree gchar *canonical_path_utf8 = g_filename_to_utf8 (canonical_path, -1,
|
||||
NULL, NULL, NULL);
|
||||
g_return_if_fail (canonical_path_utf8 != NULL);
|
||||
|
||||
if (!g_ptr_array_find_with_equal_func (_builder->paths_blacklist,
|
||||
canonical_path, g_str_equal, NULL))
|
||||
g_ptr_array_add (_builder->paths_blacklist, g_steal_pointer (&canonical_path));
|
||||
if (!g_ptr_array_find_with_equal_func (_builder->blacklist,
|
||||
canonical_path_utf8, g_str_equal, NULL))
|
||||
g_ptr_array_add (_builder->blacklist, g_steal_pointer (&canonical_path_utf8));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -691,12 +796,44 @@ mct_app_filter_builder_blacklist_flatpak_ref (MctAppFilterBuilder *builder,
|
|||
MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
|
||||
|
||||
g_return_if_fail (_builder != NULL);
|
||||
g_return_if_fail (_builder->paths_blacklist != NULL);
|
||||
g_return_if_fail (_builder->blacklist != NULL);
|
||||
g_return_if_fail (app_ref != NULL);
|
||||
g_return_if_fail (is_valid_flatpak_ref (app_ref));
|
||||
|
||||
if (!g_ptr_array_find_with_equal_func (_builder->paths_blacklist,
|
||||
if (!g_ptr_array_find_with_equal_func (_builder->blacklist,
|
||||
app_ref, g_str_equal, NULL))
|
||||
g_ptr_array_add (_builder->paths_blacklist, g_strdup (app_ref));
|
||||
g_ptr_array_add (_builder->blacklist, g_strdup (app_ref));
|
||||
}
|
||||
|
||||
/**
|
||||
* mct_app_filter_builder_blacklist_content_type:
|
||||
* @builder: an initialised #MctAppFilterBuilder
|
||||
* @content_type: a content type to blacklist
|
||||
*
|
||||
* Add @content_type to the blacklist of content types in the filter under
|
||||
* construction. The @content_type will not be added again if it’s already been
|
||||
* added.
|
||||
*
|
||||
* Note that this method doesn’t handle content subtypes. For example, if
|
||||
* `application/xml` is added to the blacklist but `application/xspf+xml` is not,
|
||||
* a check for whether `application/xspf+xml` is blacklisted would return false.
|
||||
*
|
||||
* Since: 0.4.0
|
||||
*/
|
||||
void
|
||||
mct_app_filter_builder_blacklist_content_type (MctAppFilterBuilder *builder,
|
||||
const gchar *content_type)
|
||||
{
|
||||
MctAppFilterBuilderReal *_builder = (MctAppFilterBuilderReal *) builder;
|
||||
|
||||
g_return_if_fail (_builder != NULL);
|
||||
g_return_if_fail (_builder->blacklist != NULL);
|
||||
g_return_if_fail (content_type != NULL);
|
||||
g_return_if_fail (is_valid_content_type (content_type));
|
||||
|
||||
if (!g_ptr_array_find_with_equal_func (_builder->blacklist,
|
||||
content_type, g_str_equal, NULL))
|
||||
g_ptr_array_add (_builder->blacklist, g_strdup (content_type));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
|
||||
*
|
||||
* Copyright © 2018 Endless Mobile, Inc.
|
||||
* 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
|
||||
|
@ -18,6 +18,7 @@
|
|||
*
|
||||
* Authors:
|
||||
* - Philip Withnall <withnall@endlessm.com>
|
||||
* - Andre Moreira Magalhaes <andre@endlessm.com>
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
@ -109,6 +110,8 @@ gboolean mct_app_filter_is_flatpak_app_allowed (MctAppFilter *filter,
|
|||
const gchar *app_id);
|
||||
gboolean mct_app_filter_is_appinfo_allowed (MctAppFilter *filter,
|
||||
GAppInfo *app_info);
|
||||
gboolean mct_app_filter_is_content_type_allowed (MctAppFilter *filter,
|
||||
const gchar *content_type);
|
||||
|
||||
const gchar **mct_app_filter_get_oars_sections (MctAppFilter *filter);
|
||||
MctAppFilterOarsValue mct_app_filter_get_oars_value (MctAppFilter *filter,
|
||||
|
@ -182,6 +185,9 @@ void mct_app_filter_builder_blacklist_path (MctAppFilterBuilder *builde
|
|||
const gchar *path);
|
||||
void mct_app_filter_builder_blacklist_flatpak_ref (MctAppFilterBuilder *builder,
|
||||
const gchar *app_ref);
|
||||
void mct_app_filter_builder_blacklist_content_type (MctAppFilterBuilder *builder,
|
||||
const gchar *content_type);
|
||||
|
||||
void mct_app_filter_builder_set_oars_value (MctAppFilterBuilder *builder,
|
||||
const gchar *oars_section,
|
||||
MctAppFilterOarsValue value);
|
||||
|
|
|
@ -155,6 +155,9 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture,
|
|||
mct_app_filter_builder_blacklist_flatpak_ref (fixture->builder,
|
||||
"app/org.doom.Doom/x86_64/master");
|
||||
|
||||
mct_app_filter_builder_blacklist_content_type (fixture->builder,
|
||||
"x-scheme-handler/http");
|
||||
|
||||
mct_app_filter_builder_set_oars_value (fixture->builder, "drugs-alcohol",
|
||||
MCT_APP_FILTER_OARS_VALUE_MILD);
|
||||
mct_app_filter_builder_set_oars_value (fixture->builder, "language-humor",
|
||||
|
@ -175,6 +178,11 @@ test_app_filter_builder_non_empty (BuilderFixture *fixture,
|
|||
"app/org.doom.Doom/x86_64/master"));
|
||||
g_assert_false (mct_app_filter_is_flatpak_app_allowed (filter, "org.doom.Doom"));
|
||||
|
||||
g_assert_false (mct_app_filter_is_content_type_allowed (filter,
|
||||
"x-scheme-handler/http"));
|
||||
g_assert_true (mct_app_filter_is_content_type_allowed (filter,
|
||||
"text/plain"));
|
||||
|
||||
g_assert_cmpint (mct_app_filter_get_oars_value (filter, "drugs-alcohol"), ==,
|
||||
MCT_APP_FILTER_OARS_VALUE_MILD);
|
||||
g_assert_cmpint (mct_app_filter_get_oars_value (filter, "language-humor"), ==,
|
||||
|
@ -211,6 +219,9 @@ test_app_filter_builder_empty (BuilderFixture *fixture,
|
|||
"app/org.doom.Doom/x86_64/master"));
|
||||
g_assert_true (mct_app_filter_is_flatpak_app_allowed (filter, "org.doom.Doom"));
|
||||
|
||||
g_assert_true (mct_app_filter_is_content_type_allowed (filter,
|
||||
"x-scheme-handler/http"));
|
||||
|
||||
g_assert_cmpint (mct_app_filter_get_oars_value (filter, "drugs-alcohol"), ==,
|
||||
MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
|
||||
g_assert_cmpint (mct_app_filter_get_oars_value (filter, "language-humor"), ==,
|
||||
|
@ -240,11 +251,16 @@ test_app_filter_builder_copy_empty (void)
|
|||
|
||||
mct_app_filter_builder_init (builder_copy);
|
||||
mct_app_filter_builder_blacklist_path (builder_copy, "/bin/true");
|
||||
mct_app_filter_builder_blacklist_content_type (builder_copy,
|
||||
"x-scheme-handler/http");
|
||||
filter = mct_app_filter_builder_end (builder_copy);
|
||||
|
||||
g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
|
||||
g_assert_false (mct_app_filter_is_path_allowed (filter, "/bin/true"));
|
||||
|
||||
g_assert_true (mct_app_filter_is_content_type_allowed (filter,
|
||||
"text/plain"));
|
||||
g_assert_false (mct_app_filter_is_content_type_allowed (filter,
|
||||
"x-scheme-handler/http"));
|
||||
g_assert_true (mct_app_filter_is_user_installation_allowed (filter));
|
||||
g_assert_false (mct_app_filter_is_system_installation_allowed (filter));
|
||||
}
|
||||
|
@ -259,6 +275,8 @@ test_app_filter_builder_copy_full (void)
|
|||
g_autoptr(MctAppFilter) filter = NULL;
|
||||
|
||||
mct_app_filter_builder_blacklist_path (builder, "/bin/true");
|
||||
mct_app_filter_builder_blacklist_content_type (builder,
|
||||
"x-scheme-handler/http");
|
||||
mct_app_filter_builder_set_allow_user_installation (builder, FALSE);
|
||||
mct_app_filter_builder_set_allow_system_installation (builder, TRUE);
|
||||
builder_copy = mct_app_filter_builder_copy (builder);
|
||||
|
@ -266,6 +284,10 @@ test_app_filter_builder_copy_full (void)
|
|||
|
||||
g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
|
||||
g_assert_false (mct_app_filter_is_path_allowed (filter, "/bin/true"));
|
||||
g_assert_true (mct_app_filter_is_content_type_allowed (filter,
|
||||
"text/plain"));
|
||||
g_assert_false (mct_app_filter_is_content_type_allowed (filter,
|
||||
"x-scheme-handler/http"));
|
||||
g_assert_false (mct_app_filter_is_user_installation_allowed (filter));
|
||||
g_assert_true (mct_app_filter_is_system_installation_allowed (filter));
|
||||
}
|
||||
|
@ -290,42 +312,56 @@ test_app_filter_appinfo (void)
|
|||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n" },
|
||||
/* Allowed by its path and its flatpak ID: */
|
||||
/* Allowed by its path and its content type: */
|
||||
{ TRUE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/plain\n" },
|
||||
/* Allowed by its path, its content type and its flatpak ID: */
|
||||
{ TRUE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/plain\n"
|
||||
"X-Flatpak=org.gnome.Nice\n" },
|
||||
/* Allowed by its path and its flatpak ID: */
|
||||
/* Allowed by its path, its content type and its flatpak ID: */
|
||||
{ TRUE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/plain\n"
|
||||
"X-Flatpak=org.gnome.Nice\n"
|
||||
"X-Flatpak-RenamedFrom=\n" },
|
||||
/* Allowed by its path, its flatpak ID and its old flatpak IDs: */
|
||||
/* Allowed by its path, its content type, its flatpak ID and
|
||||
* its old flatpak IDs: */
|
||||
{ TRUE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/plain\n"
|
||||
"X-Flatpak-RenamedFrom=org.gnome.OldNice\n" },
|
||||
/* Allowed by its path, its flatpak ID and its old flatpak IDs (which
|
||||
* contain some spurious entries): */
|
||||
/* Allowed by its path, its content type, its flatpak ID and its old
|
||||
* flatpak IDs (which contain some spurious entries): */
|
||||
{ TRUE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/plain\n"
|
||||
"X-Flatpak-RenamedFrom=org.gnome.OldNice;;;\n" },
|
||||
/* Allowed by its path, its flatpak ID and its old flatpak IDs: */
|
||||
/* Allowed by its path, its content type, its flatpak ID and
|
||||
* its old flatpak IDs: */
|
||||
{ TRUE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=text/plain\n"
|
||||
"X-Flatpak-RenamedFrom=org.gnome.OldNice.desktop\n" },
|
||||
/* Disallowed by its path: */
|
||||
{ FALSE,
|
||||
|
@ -333,6 +369,13 @@ test_app_filter_appinfo (void)
|
|||
"Name=Some Name\n"
|
||||
"Exec=/bin/false\n"
|
||||
"Type=Application\n" },
|
||||
/* Allowed by its path, disallowed by its content type: */
|
||||
{ FALSE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"MimeType=x-scheme-handler/http\n" },
|
||||
/* Allowed by its path, disallowed by its flatpak ID: */
|
||||
{ FALSE,
|
||||
"[Desktop Entry]\n"
|
||||
|
@ -358,10 +401,21 @@ test_app_filter_appinfo (void)
|
|||
"Type=Application\n"
|
||||
"X-Flatpak=org.gnome.WasNasty\n"
|
||||
"X-Flatpak-RenamedFrom=org.gnome.Nasty.desktop;\n" },
|
||||
/* Allowed by its path, current flatpak ID, old flatpak ID, but
|
||||
* disabled by content type: */
|
||||
{ FALSE,
|
||||
"[Desktop Entry]\n"
|
||||
"Name=Some Name\n"
|
||||
"Exec=/bin/true\n"
|
||||
"Type=Application\n"
|
||||
"X-Flatpak=org.gnome.WasNasty\n"
|
||||
"X-Flatpak-RenamedFrom=org.gnome.OldNice\n"
|
||||
"MimeType=x-scheme-handler/http\n" },
|
||||
};
|
||||
|
||||
mct_app_filter_builder_blacklist_path (&builder, "/bin/false");
|
||||
mct_app_filter_builder_blacklist_flatpak_ref (&builder, "app/org.gnome.Nasty/x86_64/stable");
|
||||
mct_app_filter_builder_blacklist_content_type (&builder, "x-scheme-handler/http");
|
||||
|
||||
filter = mct_app_filter_builder_end (&builder);
|
||||
|
||||
|
@ -584,7 +638,8 @@ test_app_filter_bus_get_whitelist (BusFixture *fixture,
|
|||
"'AppFilter': <(true, ["
|
||||
"'app/org.gnome.Whitelisted1/x86_64/stable',"
|
||||
"'app/org.gnome.Whitelisted2/x86_64/stable',"
|
||||
"'/usr/bin/true'"
|
||||
"'/usr/bin/true',"
|
||||
"'text/plain'"
|
||||
"])>,"
|
||||
"'OarsFilter': <('oars-1.1', @a{ss} {})>"
|
||||
"}"
|
||||
|
@ -611,6 +666,10 @@ test_app_filter_bus_get_whitelist (BusFixture *fixture,
|
|||
g_assert_false (mct_app_filter_is_flatpak_ref_allowed (app_filter, "app/org.gnome.Whitelisted1/x86_64/unknown"));
|
||||
g_assert_true (mct_app_filter_is_path_allowed (app_filter, "/usr/bin/true"));
|
||||
g_assert_false (mct_app_filter_is_path_allowed (app_filter, "/usr/bin/false"));
|
||||
g_assert_true (mct_app_filter_is_content_type_allowed (app_filter,
|
||||
"text/plain"));
|
||||
g_assert_false (mct_app_filter_is_content_type_allowed (app_filter,
|
||||
"x-scheme-handler/http"));
|
||||
}
|
||||
|
||||
/* Test that getting an #MctAppFilter containing all possible OARS values from
|
||||
|
@ -1097,7 +1156,7 @@ test_app_filter_bus_set (BusFixture *fixture,
|
|||
const SetAppFilterData set_app_filter_data =
|
||||
{
|
||||
.expected_uid = fixture->valid_uid,
|
||||
.expected_app_filter_value = "(false, ['/usr/bin/false', '/usr/bin/banned', 'app/org.gnome.Nasty/x86_64/stable'])",
|
||||
.expected_app_filter_value = "(false, ['/usr/bin/false', '/usr/bin/banned', 'app/org.gnome.Nasty/x86_64/stable', 'x-scheme-handler/http'])",
|
||||
.expected_oars_filter_value = "('oars-1.1', { 'violence-fantasy': 'intense' })",
|
||||
.expected_allow_user_installation_value = "true",
|
||||
.expected_allow_system_installation_value = "true",
|
||||
|
@ -1108,6 +1167,7 @@ test_app_filter_bus_set (BusFixture *fixture,
|
|||
mct_app_filter_builder_blacklist_path (&builder, "/usr/bin/false");
|
||||
mct_app_filter_builder_blacklist_path (&builder, "/usr/bin/banned");
|
||||
mct_app_filter_builder_blacklist_flatpak_ref (&builder, "app/org.gnome.Nasty/x86_64/stable");
|
||||
mct_app_filter_builder_blacklist_content_type (&builder, "x-scheme-handler/http");
|
||||
mct_app_filter_builder_set_oars_value (&builder, "violence-fantasy", MCT_APP_FILTER_OARS_VALUE_INTENSE);
|
||||
mct_app_filter_builder_set_allow_user_installation (&builder, TRUE);
|
||||
mct_app_filter_builder_set_allow_system_installation (&builder, TRUE);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.\" Manpage for malcontent\-client.
|
||||
.\" Documentation is under the same licence as the malcontent
|
||||
.\" package.
|
||||
.TH man 8 "03 Oct 2018" "1.0" "malcontent\-client man page"
|
||||
.TH man 8 "13 Jun 2019" "1.1" "malcontent\-client man page"
|
||||
.\"
|
||||
.SH NAME
|
||||
.IX Header "NAME"
|
||||
|
@ -12,7 +12,7 @@ malcontent\-client — Parental Controls Access Utility
|
|||
.\"
|
||||
\fBmalcontent\-client get [\-q] [\-n] [\fPUSER\fB]
|
||||
.PP
|
||||
\fBmalcontent\-client check [\-q] [\-n] [\fPUSER\fB] \fPPATH\fB
|
||||
\fBmalcontent\-client check [\-q] [\-n] [\fPUSER\fB] \fPARG\fB
|
||||
.\"
|
||||
.SH DESCRIPTION
|
||||
.IX Header "DESCRIPTION"
|
||||
|
@ -50,9 +50,9 @@ authorization.)
|
|||
Username or ID of the user to get the app filter for. If not specified, the
|
||||
current user will be used by default.
|
||||
.\"
|
||||
.IP "\fBPATH\fP"
|
||||
Path to a program to check against the app filter, to see if it can be run by
|
||||
the specified user.
|
||||
.IP "\fBARG\fP"
|
||||
Path to a program, content type or flatpak ref to check against the app filter,
|
||||
to see if it is allowed for the specified user.
|
||||
.\"
|
||||
.IP "\fB\-q\fP, \fB\-\-quiet\fP"
|
||||
Only output error messages, and no informational messages, as the operation
|
||||
|
@ -85,8 +85,8 @@ encounters problems.
|
|||
.IP "0" 4
|
||||
.IX Item "0"
|
||||
No problems occurred. The utility ran and successfully queried the app filter.
|
||||
If running the \fBcheck\fP command, the given path was allowed to be run by the
|
||||
given user.
|
||||
If running the \fBcheck\fP command, the given path, content type or flatpak ref
|
||||
was allowed for the given user.
|
||||
.\"
|
||||
.IP "1" 4
|
||||
.IX Item "1"
|
||||
|
@ -99,8 +99,8 @@ The current user was not authorized to query the app filter for the given user.
|
|||
.\"
|
||||
.IP "3" 4
|
||||
.IX Item "3"
|
||||
If running the \fBcheck\fP command, the given path was \fInot\fP allowed to be
|
||||
run by the given user.
|
||||
If running the \fBcheck\fP command, the given path, content type or flatpak ref
|
||||
was \fInot\fP allowed for the given user.
|
||||
.\"
|
||||
.SH BUGS
|
||||
.IX Header "BUGS"
|
||||
|
|
|
@ -194,36 +194,77 @@ def command_monitor(user, quiet=False, interactive=True):
|
|||
break
|
||||
|
||||
|
||||
def command_check(user, path, quiet=False, interactive=True):
|
||||
"""Check the given path or flatpak ref is runnable by the given user,
|
||||
according to their app filter."""
|
||||
# Simple check to check whether @arg is a valid flatpak ref - it uses the
|
||||
# same logic as 'MctAppFilter' to determine it and should be kept in sync
|
||||
# with its implementation
|
||||
def is_valid_flatpak_ref(arg):
|
||||
parts = arg.split('/')
|
||||
return (len(parts) == 4 and \
|
||||
(parts[0] == 'app' or parts[0] == 'runtime') and \
|
||||
parts[1] != '' and parts[2] != '' and parts[3] != '')
|
||||
|
||||
|
||||
# Simple check to check whether @arg is a valid content type - it uses the
|
||||
# same logic as 'MctAppFilter' to determine it and should be kept in sync
|
||||
# with its implementation
|
||||
def is_valid_content_type(arg):
|
||||
parts = arg.split('/')
|
||||
return (len(parts) == 2 and \
|
||||
parts[0] != '' and parts[1] != '')
|
||||
|
||||
|
||||
def command_check(user, arg, quiet=False, interactive=True):
|
||||
"""Check the given path, content type or flatpak ref is runnable by the
|
||||
given user, according to their app filter."""
|
||||
user_id = __lookup_user_id_or_error(user)
|
||||
app_filter = __get_app_filter_or_error(user_id, interactive)
|
||||
|
||||
if path.startswith('app/') and path.count('/') < 3:
|
||||
is_maybe_flatpak_id = arg.startswith('app/') and arg.count('/') < 3
|
||||
is_maybe_flatpak_ref = is_valid_flatpak_ref(arg)
|
||||
# Only check if arg is a valid content type if not already considered a
|
||||
# valid flatpak id, otherwise we always get multiple types recognised
|
||||
# when passing flatpak IDs as argument
|
||||
is_maybe_content_type = not is_maybe_flatpak_id and is_valid_content_type(arg)
|
||||
is_maybe_path = os.path.exists(arg)
|
||||
|
||||
recognised_types = sum([is_maybe_flatpak_id, is_maybe_flatpak_ref,
|
||||
is_maybe_content_type, is_maybe_path])
|
||||
if recognised_types == 0:
|
||||
print('Unknown argument ‘{}’'.format(arg), file=sys.stderr)
|
||||
raise SystemExit(EXIT_INVALID_OPTION)
|
||||
elif recognised_types > 1:
|
||||
print('Ambiguous argument ‘{}’ recognised as multiple types'.format(arg),
|
||||
file=sys.stderr)
|
||||
raise SystemExit(EXIT_INVALID_OPTION)
|
||||
elif is_maybe_flatpak_id:
|
||||
# Flatpak app ID
|
||||
path = path[4:]
|
||||
is_allowed = app_filter.is_flatpak_app_allowed(path)
|
||||
arg = arg[4:]
|
||||
is_allowed = app_filter.is_flatpak_app_allowed(arg)
|
||||
noun = 'Flatpak app ID'
|
||||
elif path.startswith('app/') or path.startswith('runtime/'):
|
||||
elif is_maybe_flatpak_ref:
|
||||
# Flatpak ref
|
||||
is_allowed = app_filter.is_flatpak_ref_allowed(path)
|
||||
is_allowed = app_filter.is_flatpak_ref_allowed(arg)
|
||||
noun = 'Flatpak ref'
|
||||
else:
|
||||
# File system path
|
||||
path = os.path.abspath(path)
|
||||
elif is_maybe_content_type:
|
||||
# Content type
|
||||
is_allowed = app_filter.is_content_type_allowed(arg)
|
||||
noun = 'Content type'
|
||||
elif is_maybe_path:
|
||||
path = os.path.abspath(arg)
|
||||
is_allowed = app_filter.is_path_allowed(path)
|
||||
noun = 'Path'
|
||||
else:
|
||||
raise AssertionError('code should not be reached')
|
||||
|
||||
if is_allowed:
|
||||
if not quiet:
|
||||
print('{} {} is allowed by app filter for user {}'.format(
|
||||
noun, path, user_id))
|
||||
noun, arg, user_id))
|
||||
return
|
||||
else:
|
||||
if not quiet:
|
||||
print('{} {} is not allowed by app filter for user {}'.format(
|
||||
noun, path, user_id))
|
||||
noun, arg, user_id))
|
||||
raise SystemExit(EXIT_PATH_NOT_ALLOWED)
|
||||
|
||||
|
||||
|
@ -257,10 +298,30 @@ def command_set(user, allow_user_installation=True,
|
|||
file=sys.stderr)
|
||||
raise SystemExit(EXIT_INVALID_OPTION)
|
||||
builder.set_oars_value(section, value)
|
||||
elif arg.startswith('app/') or arg.startswith('runtime/'):
|
||||
builder.blacklist_flatpak_ref(arg)
|
||||
else:
|
||||
builder.blacklist_path(arg)
|
||||
is_maybe_flatpak_ref = is_valid_flatpak_ref(arg)
|
||||
is_maybe_content_type = is_valid_content_type(arg)
|
||||
is_maybe_path = os.path.exists(arg)
|
||||
|
||||
recognised_types = sum([is_maybe_flatpak_ref,
|
||||
is_maybe_content_type, is_maybe_path])
|
||||
if recognised_types == 0:
|
||||
print('Unknown argument ‘{}’'.format(arg), file=sys.stderr)
|
||||
raise SystemExit(EXIT_INVALID_OPTION)
|
||||
elif recognised_types > 1:
|
||||
print('Ambiguous argument ‘{}’ recognised as multiple types'.format(arg),
|
||||
file=sys.stderr)
|
||||
raise SystemExit(EXIT_INVALID_OPTION)
|
||||
elif is_maybe_flatpak_ref:
|
||||
builder.blacklist_flatpak_ref(arg)
|
||||
elif is_maybe_content_type:
|
||||
builder.blacklist_content_type(arg)
|
||||
elif is_maybe_path:
|
||||
path = os.path.abspath(arg)
|
||||
builder.blacklist_path(path)
|
||||
else:
|
||||
raise AssertionError('code should not be reached')
|
||||
|
||||
app_filter = builder.end()
|
||||
|
||||
__set_app_filter_or_error(user_id, app_filter, interactive)
|
||||
|
@ -312,14 +373,16 @@ def main():
|
|||
|
||||
# ‘check’ command
|
||||
parser_check = subparsers.add_parser('check', parents=[common_parser],
|
||||
help='check whether a path is '
|
||||
help='check whether a path, content '
|
||||
'type or flatpak ref is '
|
||||
'allowed by app filter')
|
||||
parser_check.set_defaults(function=command_check)
|
||||
parser_check.add_argument('user', default='', nargs='?',
|
||||
help='user ID or username to get the app filter '
|
||||
'for (default: current user)')
|
||||
parser_check.add_argument('path',
|
||||
help='path to a program to check')
|
||||
parser_check.add_argument('arg',
|
||||
help='path to a program, content type or '
|
||||
'flatpak ref to check')
|
||||
|
||||
# ‘oars-section’ command
|
||||
parser_oars_section = subparsers.add_parser('oars-section',
|
||||
|
@ -362,7 +425,8 @@ def main():
|
|||
help='unconditionally disallow installation to '
|
||||
'the system flatpak repo')
|
||||
parser_set.add_argument('app_filter_args', nargs='*',
|
||||
help='paths to blacklist and OARS section=value '
|
||||
help='paths, content types or flatpak refs to '
|
||||
'blacklist and OARS section=value '
|
||||
'pairs to store')
|
||||
parser_set.set_defaults(allow_user_installation=True,
|
||||
allow_system_installation=False)
|
||||
|
|
Loading…
Reference in New Issue