Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2018 Endless Mobile, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this library; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : : *
21 : : * Authors:
22 : : * - Philip Withnall <withnall@endlessm.com>
23 : : */
24 : :
25 : : #include "config.h"
26 : :
27 : : #include <glib.h>
28 : : #include <gio/gdesktopappinfo.h>
29 : : #include <gio/gio.h>
30 : : #include <libmalcontent/app-filter.h>
31 : : #include <libmalcontent/manager.h>
32 : : #include <libglib-testing/dbus-queue.h>
33 : : #include <locale.h>
34 : : #include <string.h>
35 : : #include "accounts-service-iface.h"
36 : : #include "accounts-service-extension-iface.h"
37 : :
38 : :
39 : : /* Check two arrays contain exactly the same items in the same order. */
40 : : static void
41 : 12 : assert_strv_equal (const gchar * const *strv_a,
42 : : const gchar * const *strv_b)
43 : : {
44 : : gsize i;
45 : :
46 [ + + + - ]: 24 : for (i = 0; strv_a[i] != NULL && strv_b[i] != NULL; i++)
47 : 12 : g_assert_cmpstr (strv_a[i], ==, strv_b[i]);
48 : :
49 : 12 : g_assert_null (strv_a[i]);
50 : 12 : g_assert_null (strv_b[i]);
51 : 12 : }
52 : :
53 : : /* A placeholder smoketest which checks that the error quark works. */
54 : : static void
55 : 2 : test_app_filter_error_quark (void)
56 : : {
57 : 2 : g_assert_cmpint (mct_app_filter_error_quark (), !=, 0);
58 : 2 : }
59 : :
60 : : /* Test that the #GType definitions for various types work. */
61 : : static void
62 : 2 : test_app_filter_types (void)
63 : : {
64 : 2 : g_type_ensure (mct_app_filter_get_type ());
65 : 2 : g_type_ensure (mct_app_filter_builder_get_type ());
66 : 2 : }
67 : :
68 : : /* Test that ref() and unref() work on an #MctAppFilter. */
69 : : static void
70 : 2 : test_app_filter_refs (void)
71 : : {
72 : 4 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
73 : 2 : g_autoptr(MctAppFilter) filter = NULL;
74 : :
75 : : /* Use an empty #MctAppFilter. */
76 : 2 : filter = mct_app_filter_builder_end (&builder);
77 : :
78 : 2 : g_assert_nonnull (filter);
79 : :
80 : : /* Call is_path_allowed() to check that the filter hasn’t been finalised. */
81 : 2 : g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
82 : 2 : mct_app_filter_ref (filter);
83 : 2 : g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
84 : 2 : mct_app_filter_unref (filter);
85 : 2 : g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
86 : :
87 : : /* Final ref is dropped by g_autoptr(). */
88 : 2 : }
89 : :
90 : : /* Basic test of mct_app_filter_serialize() on an app filter. */
91 : : static void
92 : 2 : test_app_filter_serialize (void)
93 : : {
94 : 4 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
95 : 2 : g_autoptr(MctAppFilter) filter = NULL;
96 : 2 : g_autoptr(GVariant) serialized = NULL;
97 : :
98 : : /* Use an empty #MctAppFilter. */
99 : 2 : filter = mct_app_filter_builder_end (&builder);
100 : :
101 : : /* We can’t assert anything about the serialisation format, since it’s opaque. */
102 : 2 : serialized = mct_app_filter_serialize (filter);
103 : 2 : g_assert_nonnull (serialized);
104 : 2 : }
105 : :
106 : : /* Basic test of mct_app_filter_deserialize() on various current and historic
107 : : * serialised app filter variants. */
108 : : static void
109 : 2 : test_app_filter_deserialize (void)
110 : : {
111 : : /* These are all opaque. Older versions should be kept around to test
112 : : * backwards compatibility. */
113 : 2 : const gchar *valid_app_filters[] =
114 : : {
115 : : "@a{sv} {}",
116 : : "{ 'AppFilter': <(true, @as [])> }",
117 : : "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild' })> }",
118 : : "{ 'AllowUserInstallation': <true> }",
119 : : "{ 'AllowSystemInstallation': <true> }",
120 : : };
121 : :
122 [ + + ]: 12 : for (gsize i = 0; i < G_N_ELEMENTS (valid_app_filters); i++)
123 : : {
124 : 10 : g_autoptr(GVariant) serialized = NULL;
125 : 10 : g_autoptr(MctAppFilter) filter = NULL;
126 : 10 : g_autoptr(GError) local_error = NULL;
127 : :
128 : 10 : g_test_message ("%" G_GSIZE_FORMAT ": %s", i, valid_app_filters[i]);
129 : :
130 : 10 : serialized = g_variant_parse (NULL, valid_app_filters[i], NULL, NULL, NULL);
131 : 10 : g_assert (serialized != NULL);
132 : :
133 : 10 : filter = mct_app_filter_deserialize (serialized, 1, &local_error);
134 : 10 : g_assert_no_error (local_error);
135 : 10 : g_assert_nonnull (filter);
136 : : }
137 : 2 : }
138 : :
139 : : /* Test of mct_app_filter_deserialize() on various invalid variants. */
140 : : static void
141 : 2 : test_app_filter_deserialize_invalid (void)
142 : : {
143 : 2 : const gchar *invalid_app_filters[] =
144 : : {
145 : : "false",
146 : : "()",
147 : : "{ 'OarsFilter': <('invalid', { 'violence-cartoon': 'mild' })> }",
148 : : };
149 : :
150 [ + + ]: 8 : for (gsize i = 0; i < G_N_ELEMENTS (invalid_app_filters); i++)
151 : : {
152 : 6 : g_autoptr(GVariant) serialized = NULL;
153 : 6 : g_autoptr(MctAppFilter) filter = NULL;
154 : 6 : g_autoptr(GError) local_error = NULL;
155 : :
156 : 6 : g_test_message ("%" G_GSIZE_FORMAT ": %s", i, invalid_app_filters[i]);
157 : :
158 : 6 : serialized = g_variant_parse (NULL, invalid_app_filters[i], NULL, NULL, NULL);
159 : 6 : g_assert (serialized != NULL);
160 : :
161 : 6 : filter = mct_app_filter_deserialize (serialized, 1, &local_error);
162 : 6 : g_assert_error (local_error, MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_DATA);
163 : 6 : g_assert_null (filter);
164 : : }
165 : 2 : }
166 : :
167 : : /* Test that mct_app_filter_equal() returns the correct results on various
168 : : * app filters. */
169 : : static void
170 : 2 : test_app_filter_equal (void)
171 : : {
172 : 4 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
173 : : MctAppFilter *equal_filters[2];
174 : 2 : const gchar *unequal_filters_serialised[] =
175 : : {
176 : : "{ 'AppFilter': <(true, @as ['/usr/bin/gnome-software'])> }",
177 : : "{ 'AppFilter': <(false, @as ['/usr/bin/gnome-software'])> }",
178 : : "{ 'AllowUserInstallation': <true> }",
179 : : "{ 'AllowSystemInstallation': <true> }",
180 : : "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild' })> }",
181 : : };
182 : : MctAppFilter *unequal_filters[G_N_ELEMENTS (unequal_filters_serialised)];
183 : :
184 : : /* Build a couple of filters which are identical. */
185 : 2 : equal_filters[0] = mct_app_filter_builder_end (&builder);
186 : :
187 : 2 : mct_app_filter_builder_init (&builder);
188 : 2 : equal_filters[1] = mct_app_filter_builder_end (&builder);
189 : :
190 : : /* And a load of filters which are not. */
191 [ + + ]: 12 : for (gsize i = 0; i < G_N_ELEMENTS (unequal_filters_serialised); i++)
192 : : {
193 : 10 : g_autoptr(GVariant) serialized = NULL;
194 : :
195 : 10 : serialized = g_variant_parse (NULL, unequal_filters_serialised[i], NULL, NULL, NULL);
196 : 10 : g_assert (serialized != NULL);
197 : :
198 : 10 : unequal_filters[i] = mct_app_filter_deserialize (serialized, 1, NULL);
199 : 10 : g_assert (unequal_filters[i] != NULL);
200 : : }
201 : :
202 : : /* Test the equality checks on them all. */
203 [ + + ]: 6 : for (gsize i = 0; i < G_N_ELEMENTS (equal_filters); i++)
204 [ + + ]: 12 : for (gsize j = 0; j < G_N_ELEMENTS (equal_filters); j++)
205 : 8 : g_assert_true (mct_app_filter_equal (equal_filters[i], equal_filters[j]));
206 : :
207 [ + + ]: 12 : for (gsize i = 0; i < G_N_ELEMENTS (unequal_filters); i++)
208 : : {
209 [ + + ]: 30 : for (gsize j = 0; j < G_N_ELEMENTS (equal_filters); j++)
210 : 20 : g_assert_false (mct_app_filter_equal (unequal_filters[i], equal_filters[j]));
211 [ + + ]: 60 : for (gsize j = 0; j < G_N_ELEMENTS (unequal_filters); j++)
212 : : {
213 [ + + ]: 50 : if (i != j)
214 : 40 : g_assert_false (mct_app_filter_equal (unequal_filters[i], unequal_filters[j]));
215 : : else
216 : 10 : g_assert_true (mct_app_filter_equal (unequal_filters[i], unequal_filters[j]));
217 : : }
218 : : }
219 : :
220 [ + + ]: 6 : for (gsize i = 0; i < G_N_ELEMENTS (equal_filters); i++)
221 : 4 : mct_app_filter_unref (equal_filters[i]);
222 [ + + ]: 12 : for (gsize i = 0; i < G_N_ELEMENTS (unequal_filters); i++)
223 : 10 : mct_app_filter_unref (unequal_filters[i]);
224 : 2 : }
225 : :
226 : : /* Test that mct_app_filter_is_enabled() returns the correct results on various
227 : : * app filters. */
228 : : static void
229 : 2 : test_app_filter_is_enabled (void)
230 : : {
231 : : const struct
232 : : {
233 : : const gchar *serialized;
234 : : gboolean is_enabled;
235 : : }
236 : 2 : app_filters[] =
237 : : {
238 : : { "@a{sv} {}", FALSE },
239 : : { "{ 'AppFilter': <(true, @as [])> }", TRUE },
240 : : { "{ 'AppFilter': <(false, @as [])> }", FALSE },
241 : : { "{ 'AppFilter': <(false, @as [ '/usr/bin/gnome-software' ])> }", TRUE },
242 : : { "{ 'OarsFilter': <('oars-1.1', @a{ss} {})> }", FALSE },
243 : : { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild' })> }", TRUE },
244 : : { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'intense' })> }", FALSE },
245 : : { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': '' })> }", FALSE }, /* technically an invalid serialisation */
246 : : { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'none' })> }", TRUE },
247 : : { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild', 'violence-realistic': 'intense' })> }", TRUE },
248 : : { "{ 'OarsFilter': <('oars-1.1', { 'violence-cartoon': 'mild', 'violence-realistic': 'none' })> }", TRUE },
249 : : { "{ 'AllowUserInstallation': <true> }", FALSE },
250 : : { "{ 'AllowUserInstallation': <false> }", TRUE },
251 : : { "{ 'AllowSystemInstallation': <true> }", FALSE },
252 : : { "{ 'AllowSystemInstallation': <false> }", FALSE },
253 : : };
254 : :
255 [ + + ]: 32 : for (gsize i = 0; i < G_N_ELEMENTS (app_filters); i++)
256 : : {
257 : 30 : g_autoptr(GVariant) variant = NULL;
258 : 30 : g_autoptr(MctAppFilter) filter = NULL;
259 : :
260 : 30 : g_test_message ("%" G_GSIZE_FORMAT ": %s", i, app_filters[i].serialized);
261 : :
262 : 30 : variant = g_variant_parse (NULL, app_filters[i].serialized, NULL, NULL, NULL);
263 : 30 : g_assert (variant != NULL);
264 : :
265 : 30 : filter = mct_app_filter_deserialize (variant, 1, NULL);
266 : 30 : g_assert (filter != NULL);
267 : :
268 [ + + ]: 30 : if (app_filters[i].is_enabled)
269 : 14 : g_assert_true (mct_app_filter_is_enabled (filter));
270 : : else
271 : 16 : g_assert_false (mct_app_filter_is_enabled (filter));
272 : : }
273 : 2 : }
274 : :
275 : : /* Fixture for tests which use an #MctAppFilterBuilder. The builder can either
276 : : * be heap- or stack-allocated. @builder will always be a valid pointer to it.
277 : : */
278 : : typedef struct
279 : : {
280 : : MctAppFilterBuilder *builder;
281 : : MctAppFilterBuilder stack_builder;
282 : : } BuilderFixture;
283 : :
284 : : static void
285 : 4 : builder_set_up_stack (BuilderFixture *fixture,
286 : : gconstpointer test_data)
287 : : {
288 : 4 : mct_app_filter_builder_init (&fixture->stack_builder);
289 : 4 : fixture->builder = &fixture->stack_builder;
290 : 4 : }
291 : :
292 : : static void
293 : 4 : builder_tear_down_stack (BuilderFixture *fixture,
294 : : gconstpointer test_data)
295 : : {
296 : 4 : mct_app_filter_builder_clear (&fixture->stack_builder);
297 : 4 : fixture->builder = NULL;
298 : 4 : }
299 : :
300 : : static void
301 : 4 : builder_set_up_stack2 (BuilderFixture *fixture,
302 : : gconstpointer test_data)
303 : : {
304 : 4 : MctAppFilterBuilder local_builder = MCT_APP_FILTER_BUILDER_INIT ();
305 : 4 : memcpy (&fixture->stack_builder, &local_builder, sizeof (local_builder));
306 : 4 : fixture->builder = &fixture->stack_builder;
307 : 4 : }
308 : :
309 : : static void
310 : 4 : builder_tear_down_stack2 (BuilderFixture *fixture,
311 : : gconstpointer test_data)
312 : : {
313 : 4 : mct_app_filter_builder_clear (&fixture->stack_builder);
314 : 4 : fixture->builder = NULL;
315 : 4 : }
316 : :
317 : : static void
318 : 4 : builder_set_up_heap (BuilderFixture *fixture,
319 : : gconstpointer test_data)
320 : : {
321 : 4 : fixture->builder = mct_app_filter_builder_new ();
322 : 4 : }
323 : :
324 : : static void
325 : 4 : builder_tear_down_heap (BuilderFixture *fixture,
326 : : gconstpointer test_data)
327 : : {
328 [ + - ]: 4 : g_clear_pointer (&fixture->builder, mct_app_filter_builder_free);
329 : 4 : }
330 : :
331 : : /* Test building a non-empty #MctAppFilter using an #MctAppFilterBuilder. */
332 : : static void
333 : 6 : test_app_filter_builder_non_empty (BuilderFixture *fixture,
334 : : gconstpointer test_data)
335 : : {
336 : 6 : g_autoptr(MctAppFilter) filter = NULL;
337 : 6 : g_autofree const gchar **sections = NULL;
338 : :
339 : 6 : mct_app_filter_builder_blocklist_path (fixture->builder, "/bin/true");
340 : 6 : mct_app_filter_builder_blocklist_path (fixture->builder, "/usr/bin/gnome-software");
341 : :
342 : 6 : mct_app_filter_builder_blocklist_flatpak_ref (fixture->builder,
343 : : "app/org.doom.Doom/x86_64/stable");
344 : :
345 : 6 : mct_app_filter_builder_blocklist_content_type (fixture->builder,
346 : : "x-scheme-handler/http");
347 : :
348 : 6 : mct_app_filter_builder_set_oars_value (fixture->builder, "drugs-alcohol",
349 : : MCT_APP_FILTER_OARS_VALUE_MILD);
350 : 6 : mct_app_filter_builder_set_oars_value (fixture->builder, "language-humor",
351 : : MCT_APP_FILTER_OARS_VALUE_MODERATE);
352 : 6 : mct_app_filter_builder_set_allow_user_installation (fixture->builder, TRUE);
353 : 6 : mct_app_filter_builder_set_allow_system_installation (fixture->builder, FALSE);
354 : :
355 : 6 : filter = mct_app_filter_builder_end (fixture->builder);
356 : :
357 : 6 : g_assert_true (mct_app_filter_is_enabled (filter));
358 : :
359 : 6 : g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
360 : 6 : g_assert_false (mct_app_filter_is_path_allowed (filter,
361 : : "/usr/bin/gnome-software"));
362 : :
363 : 6 : g_assert_true (mct_app_filter_is_flatpak_ref_allowed (filter,
364 : : "app/org.gnome.Ponies/x86_64/stable"));
365 : 6 : g_assert_true (mct_app_filter_is_flatpak_app_allowed (filter, "org.gnome.Ponies"));
366 : 6 : g_assert_false (mct_app_filter_is_flatpak_ref_allowed (filter,
367 : : "app/org.doom.Doom/x86_64/stable"));
368 : 6 : g_assert_false (mct_app_filter_is_flatpak_app_allowed (filter, "org.doom.Doom"));
369 : :
370 : 6 : g_assert_false (mct_app_filter_is_content_type_allowed (filter,
371 : : "x-scheme-handler/http"));
372 : 6 : g_assert_true (mct_app_filter_is_content_type_allowed (filter,
373 : : "text/plain"));
374 : :
375 : 6 : g_assert_cmpint (mct_app_filter_get_oars_value (filter, "drugs-alcohol"), ==,
376 : : MCT_APP_FILTER_OARS_VALUE_MILD);
377 : 6 : g_assert_cmpint (mct_app_filter_get_oars_value (filter, "language-humor"), ==,
378 : : MCT_APP_FILTER_OARS_VALUE_MODERATE);
379 : 6 : g_assert_cmpint (mct_app_filter_get_oars_value (filter, "something-else"), ==,
380 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
381 : :
382 : 6 : sections = mct_app_filter_get_oars_sections (filter);
383 : 6 : const gchar * const expected_sections[] = { "drugs-alcohol", "language-humor", NULL };
384 : 6 : assert_strv_equal ((const gchar * const *) sections, expected_sections);
385 : :
386 : 6 : g_assert_true (mct_app_filter_is_user_installation_allowed (filter));
387 : 6 : g_assert_false (mct_app_filter_is_system_installation_allowed (filter));
388 : 6 : }
389 : :
390 : : /* Test building an empty #MctAppFilter using an #MctAppFilterBuilder. */
391 : : static void
392 : 6 : test_app_filter_builder_empty (BuilderFixture *fixture,
393 : : gconstpointer test_data)
394 : : {
395 : 6 : g_autoptr(MctAppFilter) filter = NULL;
396 : 6 : g_autofree const gchar **sections = NULL;
397 : :
398 : 6 : filter = mct_app_filter_builder_end (fixture->builder);
399 : :
400 : 6 : g_assert_false (mct_app_filter_is_enabled (filter));
401 : :
402 : 6 : g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
403 : 6 : g_assert_true (mct_app_filter_is_path_allowed (filter,
404 : : "/usr/bin/gnome-software"));
405 : :
406 : 6 : g_assert_true (mct_app_filter_is_flatpak_ref_allowed (filter,
407 : : "app/org.gnome.Ponies/x86_64/stable"));
408 : 6 : g_assert_true (mct_app_filter_is_flatpak_app_allowed (filter, "org.gnome.Ponies"));
409 : 6 : g_assert_true (mct_app_filter_is_flatpak_ref_allowed (filter,
410 : : "app/org.doom.Doom/x86_64/stable"));
411 : 6 : g_assert_true (mct_app_filter_is_flatpak_app_allowed (filter, "org.doom.Doom"));
412 : :
413 : 6 : g_assert_true (mct_app_filter_is_content_type_allowed (filter,
414 : : "x-scheme-handler/http"));
415 : :
416 : 6 : g_assert_cmpint (mct_app_filter_get_oars_value (filter, "drugs-alcohol"), ==,
417 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
418 : 6 : g_assert_cmpint (mct_app_filter_get_oars_value (filter, "language-humor"), ==,
419 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
420 : 6 : g_assert_cmpint (mct_app_filter_get_oars_value (filter, "something-else"), ==,
421 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
422 : :
423 : 6 : sections = mct_app_filter_get_oars_sections (filter);
424 : 6 : const gchar * const expected_sections[] = { NULL };
425 : 6 : assert_strv_equal ((const gchar * const *) sections, expected_sections);
426 : :
427 : 6 : g_assert_true (mct_app_filter_is_user_installation_allowed (filter));
428 : 6 : g_assert_false (mct_app_filter_is_system_installation_allowed (filter));
429 : 6 : }
430 : :
431 : : /* Check that copying a cleared #MctAppFilterBuilder works, and the copy can
432 : : * then be initialised and used to build a filter. */
433 : : static void
434 : 2 : test_app_filter_builder_copy_empty (void)
435 : : {
436 : 4 : g_autoptr(MctAppFilterBuilder) builder = mct_app_filter_builder_new ();
437 : 2 : g_autoptr(MctAppFilterBuilder) builder_copy = NULL;
438 : 2 : g_autoptr(MctAppFilter) filter = NULL;
439 : :
440 : 2 : mct_app_filter_builder_clear (builder);
441 : 2 : builder_copy = mct_app_filter_builder_copy (builder);
442 : :
443 : 2 : mct_app_filter_builder_init (builder_copy);
444 : 2 : mct_app_filter_builder_blocklist_path (builder_copy, "/bin/true");
445 : 2 : mct_app_filter_builder_blocklist_content_type (builder_copy,
446 : : "x-scheme-handler/http");
447 : 2 : filter = mct_app_filter_builder_end (builder_copy);
448 : :
449 : 2 : g_assert_true (mct_app_filter_is_enabled (filter));
450 : 2 : g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
451 : 2 : g_assert_false (mct_app_filter_is_path_allowed (filter, "/bin/true"));
452 : 2 : g_assert_true (mct_app_filter_is_content_type_allowed (filter,
453 : : "text/plain"));
454 : 2 : g_assert_false (mct_app_filter_is_content_type_allowed (filter,
455 : : "x-scheme-handler/http"));
456 : 2 : g_assert_true (mct_app_filter_is_user_installation_allowed (filter));
457 : 2 : g_assert_false (mct_app_filter_is_system_installation_allowed (filter));
458 : 2 : }
459 : :
460 : : /* Check that copying a filled #MctAppFilterBuilder works, and the copy can be
461 : : * used to build a filter. */
462 : : static void
463 : 2 : test_app_filter_builder_copy_full (void)
464 : : {
465 : 4 : g_autoptr(MctAppFilterBuilder) builder = mct_app_filter_builder_new ();
466 : 2 : g_autoptr(MctAppFilterBuilder) builder_copy = NULL;
467 : 2 : g_autoptr(MctAppFilter) filter = NULL;
468 : :
469 : 2 : mct_app_filter_builder_blocklist_path (builder, "/bin/true");
470 : 2 : mct_app_filter_builder_blocklist_content_type (builder,
471 : : "x-scheme-handler/http");
472 : 2 : mct_app_filter_builder_set_allow_user_installation (builder, FALSE);
473 : 2 : mct_app_filter_builder_set_allow_system_installation (builder, TRUE);
474 : 2 : builder_copy = mct_app_filter_builder_copy (builder);
475 : 2 : filter = mct_app_filter_builder_end (builder_copy);
476 : :
477 : 2 : g_assert_true (mct_app_filter_is_enabled (filter));
478 : 2 : g_assert_true (mct_app_filter_is_path_allowed (filter, "/bin/false"));
479 : 2 : g_assert_false (mct_app_filter_is_path_allowed (filter, "/bin/true"));
480 : 2 : g_assert_true (mct_app_filter_is_content_type_allowed (filter,
481 : : "text/plain"));
482 : 2 : g_assert_false (mct_app_filter_is_content_type_allowed (filter,
483 : : "x-scheme-handler/http"));
484 : 2 : g_assert_false (mct_app_filter_is_user_installation_allowed (filter));
485 : 2 : g_assert_true (mct_app_filter_is_system_installation_allowed (filter));
486 : 2 : }
487 : :
488 : : /* Check that various configurations of a #GAppInfo are accepted or rejected
489 : : * as appropriate by mct_app_filter_is_appinfo_allowed(). */
490 : : static void
491 : 2 : test_app_filter_appinfo (void)
492 : : {
493 : 4 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
494 : 2 : g_autoptr(MctAppFilter) filter = NULL;
495 : : const struct
496 : : {
497 : : gboolean expected_allowed;
498 : : const gchar *key_file_data;
499 : : }
500 : 2 : vectors[] =
501 : : {
502 : : /* Allowed by its path: */
503 : : { TRUE,
504 : : "[Desktop Entry]\n"
505 : : "Name=Some Name\n"
506 : : "Exec=/bin/true\n"
507 : : "Type=Application\n" },
508 : : /* Allowed by its path and its content type: */
509 : : { TRUE,
510 : : "[Desktop Entry]\n"
511 : : "Name=Some Name\n"
512 : : "Exec=/bin/true\n"
513 : : "Type=Application\n"
514 : : "MimeType=text/plain\n" },
515 : : /* Allowed by its path, its content type and its flatpak ID: */
516 : : { TRUE,
517 : : "[Desktop Entry]\n"
518 : : "Name=Some Name\n"
519 : : "Exec=/bin/true\n"
520 : : "Type=Application\n"
521 : : "MimeType=text/plain\n"
522 : : "X-Flatpak=org.gnome.Nice\n" },
523 : : /* Allowed by its path, its content type and its flatpak ID: */
524 : : { TRUE,
525 : : "[Desktop Entry]\n"
526 : : "Name=Some Name\n"
527 : : "Exec=/bin/true\n"
528 : : "Type=Application\n"
529 : : "MimeType=text/plain\n"
530 : : "X-Flatpak=org.gnome.Nice\n"
531 : : "X-Flatpak-RenamedFrom=\n" },
532 : : /* Allowed by its path, its content type, its flatpak ID and
533 : : * its old flatpak IDs: */
534 : : { TRUE,
535 : : "[Desktop Entry]\n"
536 : : "Name=Some Name\n"
537 : : "Exec=/bin/true\n"
538 : : "Type=Application\n"
539 : : "MimeType=text/plain\n"
540 : : "X-Flatpak-RenamedFrom=org.gnome.OldNice\n" },
541 : : /* Allowed by its path, its content type, its flatpak ID and its old
542 : : * flatpak IDs (which contain some spurious entries): */
543 : : { TRUE,
544 : : "[Desktop Entry]\n"
545 : : "Name=Some Name\n"
546 : : "Exec=/bin/true\n"
547 : : "Type=Application\n"
548 : : "MimeType=text/plain\n"
549 : : "X-Flatpak-RenamedFrom=org.gnome.OldNice;;;\n" },
550 : : /* Allowed by its path, its content type, its flatpak ID and
551 : : * its old flatpak IDs: */
552 : : { TRUE,
553 : : "[Desktop Entry]\n"
554 : : "Name=Some Name\n"
555 : : "Exec=/bin/true\n"
556 : : "Type=Application\n"
557 : : "MimeType=text/plain\n"
558 : : "X-Flatpak-RenamedFrom=org.gnome.OldNice.desktop\n" },
559 : : /* Disallowed by its path: */
560 : : { FALSE,
561 : : "[Desktop Entry]\n"
562 : : "Name=Some Name\n"
563 : : "Exec=/bin/false\n"
564 : : "Type=Application\n" },
565 : : /* Allowed by its path, disallowed by its content type: */
566 : : { FALSE,
567 : : "[Desktop Entry]\n"
568 : : "Name=Some Name\n"
569 : : "Exec=/bin/true\n"
570 : : "Type=Application\n"
571 : : "MimeType=x-scheme-handler/http\n" },
572 : : /* Allowed by its path, disallowed by its flatpak ID: */
573 : : { FALSE,
574 : : "[Desktop Entry]\n"
575 : : "Name=Some Name\n"
576 : : "Exec=/bin/true\n"
577 : : "Type=Application\n"
578 : : "X-Flatpak=org.gnome.Nasty\n" },
579 : : /* Allowed by its path and current flatpak ID, but disallowed by an old
580 : : * flatpak ID: */
581 : : { FALSE,
582 : : "[Desktop Entry]\n"
583 : : "Name=Some Name\n"
584 : : "Exec=/bin/true\n"
585 : : "Type=Application\n"
586 : : "X-Flatpak=org.gnome.WasNasty\n"
587 : : "X-Flatpak-RenamedFrom= org.gnome.OlderNasty ; org.gnome.Nasty ; \n" },
588 : : /* Allowed by its path and current flatpak ID, but disallowed by an old
589 : : * flatpak ID: */
590 : : { FALSE,
591 : : "[Desktop Entry]\n"
592 : : "Name=Some Name\n"
593 : : "Exec=/bin/true\n"
594 : : "Type=Application\n"
595 : : "X-Flatpak=org.gnome.WasNasty\n"
596 : : "X-Flatpak-RenamedFrom=org.gnome.Nasty.desktop;\n" },
597 : : /* Allowed by its path, current flatpak ID, old flatpak ID, but
598 : : * disabled by content type: */
599 : : { FALSE,
600 : : "[Desktop Entry]\n"
601 : : "Name=Some Name\n"
602 : : "Exec=/bin/true\n"
603 : : "Type=Application\n"
604 : : "X-Flatpak=org.gnome.WasNasty\n"
605 : : "X-Flatpak-RenamedFrom=org.gnome.OldNice\n"
606 : : "MimeType=x-scheme-handler/http\n" },
607 : : };
608 : :
609 : 2 : mct_app_filter_builder_blocklist_path (&builder, "/bin/false");
610 : 2 : mct_app_filter_builder_blocklist_flatpak_ref (&builder, "app/org.gnome.Nasty/x86_64/stable");
611 : 2 : mct_app_filter_builder_blocklist_content_type (&builder, "x-scheme-handler/http");
612 : :
613 : 2 : filter = mct_app_filter_builder_end (&builder);
614 : :
615 [ + + ]: 28 : for (gsize i = 0; i < G_N_ELEMENTS (vectors); i++)
616 : : {
617 : 26 : g_autoptr(GKeyFile) key_file = NULL;
618 : 26 : g_autoptr(GError) local_error = NULL;
619 : 26 : g_autoptr(GAppInfo) appinfo = NULL;
620 : :
621 : 26 : g_test_message ("Vector %" G_GSIZE_FORMAT ": %s",
622 : 26 : i, vectors[i].key_file_data);
623 : :
624 : 26 : key_file = g_key_file_new ();
625 : 26 : g_key_file_load_from_data (key_file, vectors[i].key_file_data, -1,
626 : : G_KEY_FILE_NONE, &local_error);
627 : 26 : g_assert_no_error (local_error);
628 : :
629 : 26 : appinfo = G_APP_INFO (g_desktop_app_info_new_from_keyfile (key_file));
630 : 26 : g_assert_nonnull (appinfo);
631 : :
632 [ + + ]: 26 : if (vectors[i].expected_allowed)
633 : 14 : g_assert_true (mct_app_filter_is_appinfo_allowed (filter, appinfo));
634 : : else
635 : 12 : g_assert_false (mct_app_filter_is_appinfo_allowed (filter, appinfo));
636 : : }
637 : 2 : }
638 : :
639 : : /* Fixture for tests which interact with the accountsservice over D-Bus. The
640 : : * D-Bus service is mocked up using @queue, which allows us to reply to D-Bus
641 : : * calls from the code under test from within the test process.
642 : : *
643 : : * It exports one user object (for UID 500) and the manager object. The method
644 : : * return values from UID 500 are up to the test in question, so it could be an
645 : : * administrator, or non-administrator, have a restrictive or permissive app
646 : : * filter, etc.
647 : : */
648 : : typedef struct
649 : : {
650 : : GtDBusQueue *queue; /* (owned) */
651 : : uid_t valid_uid;
652 : : uid_t missing_uid;
653 : : MctManager *manager; /* (owned) */
654 : : } BusFixture;
655 : :
656 : : static void
657 : 20 : bus_set_up (BusFixture *fixture,
658 : : gconstpointer test_data)
659 : : {
660 : 19 : g_autoptr(GError) local_error = NULL;
661 : 39 : g_autofree gchar *object_path = NULL;
662 : :
663 : 20 : fixture->valid_uid = 500; /* arbitrarily chosen */
664 : 20 : fixture->missing_uid = 501; /* must be different from valid_uid and not exported */
665 : 20 : fixture->queue = gt_dbus_queue_new ();
666 : :
667 : 20 : gt_dbus_queue_connect (fixture->queue, &local_error);
668 : 19 : g_assert_no_error (local_error);
669 : :
670 : 19 : gt_dbus_queue_own_name (fixture->queue, "org.freedesktop.Accounts");
671 : :
672 : 19 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", fixture->valid_uid);
673 : 19 : gt_dbus_queue_export_object (fixture->queue,
674 : : object_path,
675 : : (GDBusInterfaceInfo *) &com_endlessm_parental_controls_app_filter_interface,
676 : : &local_error);
677 : 19 : g_assert_no_error (local_error);
678 : :
679 : 19 : gt_dbus_queue_export_object (fixture->queue,
680 : : "/org/freedesktop/Accounts",
681 : : (GDBusInterfaceInfo *) &org_freedesktop_accounts_interface,
682 : : &local_error);
683 : 19 : g_assert_no_error (local_error);
684 : :
685 : 19 : fixture->manager = mct_manager_new (gt_dbus_queue_get_client_connection (fixture->queue));
686 : 19 : }
687 : :
688 : : static void
689 : 19 : bus_tear_down (BusFixture *fixture,
690 : : gconstpointer test_data)
691 : : {
692 [ + - ]: 19 : g_clear_object (&fixture->manager);
693 : 19 : gt_dbus_queue_disconnect (fixture->queue, TRUE);
694 [ + - ]: 19 : g_clear_pointer (&fixture->queue, gt_dbus_queue_free);
695 : 19 : }
696 : :
697 : : /* Helper #GAsyncReadyCallback which returns the #GAsyncResult in its @user_data. */
698 : : static void
699 : 8 : async_result_cb (GObject *obj,
700 : : GAsyncResult *result,
701 : : gpointer user_data)
702 : : {
703 : 8 : GAsyncResult **result_out = (GAsyncResult **) user_data;
704 : :
705 : 8 : g_assert_null (*result_out);
706 : 8 : *result_out = g_object_ref (result);
707 : 8 : }
708 : :
709 : : /* Generic mock accountsservice implementation which returns the properties
710 : : * given in #GetAppFilterData.properties if queried for a UID matching
711 : : * #GetAppFilterData.expected_uid. Intended to be used for writing ‘successful’
712 : : * mct_manager_get_app_filter() tests returning a variety of values. */
713 : : typedef struct
714 : : {
715 : : uid_t expected_uid;
716 : : const gchar *properties;
717 : : } GetAppFilterData;
718 : :
719 : : /* This is run in a worker thread. */
720 : : static void
721 : 5 : get_app_filter_server_cb (GtDBusQueue *queue,
722 : : gpointer user_data)
723 : : {
724 : 5 : const GetAppFilterData *data = user_data;
725 : 5 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
726 : 5 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
727 : 5 : g_autofree gchar *object_path = NULL;
728 : 5 : g_autoptr(GVariant) properties_variant = NULL;
729 : :
730 : : /* Handle the FindUserById() call. */
731 : : gint64 user_id;
732 : 5 : invocation1 =
733 : 5 : gt_dbus_queue_assert_pop_message (queue,
734 : : "/org/freedesktop/Accounts",
735 : : "org.freedesktop.Accounts",
736 : : "FindUserById", "(x)", &user_id);
737 : 5 : g_assert_cmpint (user_id, ==, data->expected_uid);
738 : :
739 : 5 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
740 : 5 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
741 : :
742 : : /* Handle the Properties.GetAll() call and return some arbitrary, valid values
743 : : * for the given user. */
744 : : const gchar *property_interface;
745 : 5 : invocation2 =
746 : 5 : gt_dbus_queue_assert_pop_message (queue,
747 : : object_path,
748 : : "org.freedesktop.DBus.Properties",
749 : : "GetAll", "(&s)", &property_interface);
750 : 5 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.AppFilter");
751 : :
752 : 5 : properties_variant = g_variant_ref_sink (g_variant_new_parsed (data->properties));
753 : 5 : g_dbus_method_invocation_return_value (invocation2,
754 : : g_variant_new_tuple (&properties_variant, 1));
755 : 5 : }
756 : :
757 : : /* Test that getting an #MctAppFilter from the mock D-Bus service works. The
758 : : * @test_data is a boolean value indicating whether to do the call
759 : : * synchronously (%FALSE) or asynchronously (%TRUE).
760 : : *
761 : : * The mock D-Bus replies are generated in get_app_filter_server_cb(), which is
762 : : * used for both synchronous and asynchronous calls. */
763 : : static void
764 : 2 : test_app_filter_bus_get (BusFixture *fixture,
765 : : gconstpointer test_data)
766 : : {
767 : 2 : g_autoptr(MctAppFilter) app_filter = NULL;
768 : 2 : g_autoptr(GError) local_error = NULL;
769 : 2 : gboolean test_async = GPOINTER_TO_UINT (test_data);
770 : 2 : const GetAppFilterData get_app_filter_data =
771 : : {
772 : 2 : .expected_uid = fixture->valid_uid,
773 : : .properties = "{"
774 : : "'AllowUserInstallation': <true>,"
775 : : "'AllowSystemInstallation': <false>,"
776 : : "'AppFilter': <(false, ['app/org.gnome.Builder/x86_64/stable'])>,"
777 : : "'OarsFilter': <('oars-1.1', { 'violence-bloodshed': 'mild' })>"
778 : : "}"
779 : : };
780 : :
781 : 2 : gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb,
782 : : (gpointer) &get_app_filter_data);
783 : :
784 [ + + ]: 2 : if (test_async)
785 : : {
786 : 2 : g_autoptr(GAsyncResult) result = NULL;
787 : :
788 : 1 : mct_manager_get_app_filter_async (fixture->manager,
789 : : fixture->valid_uid,
790 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
791 : : async_result_cb, &result);
792 : :
793 [ + + ]: 4 : while (result == NULL)
794 : 3 : g_main_context_iteration (NULL, TRUE);
795 : 1 : app_filter = mct_manager_get_app_filter_finish (fixture->manager, result, &local_error);
796 : : }
797 : : else
798 : : {
799 : 1 : app_filter = mct_manager_get_app_filter (fixture->manager,
800 : : fixture->valid_uid,
801 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
802 : : &local_error);
803 : : }
804 : :
805 : 2 : g_assert_no_error (local_error);
806 : 2 : g_assert_nonnull (app_filter);
807 : :
808 : : /* Check the app filter properties. */
809 : 2 : g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid);
810 : 2 : g_assert_true (mct_app_filter_is_enabled (app_filter));
811 : 2 : g_assert_false (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Builder"));
812 : 2 : g_assert_true (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Chess"));
813 : 2 : }
814 : :
815 : : /* Test that getting an #MctAppFilter containing a allowlist from the mock D-Bus
816 : : * service works, and that the #MctAppFilter methods handle the allowlist
817 : : * correctly.
818 : : *
819 : : * The mock D-Bus replies are generated in get_app_filter_server_cb(). */
820 : : static void
821 : 1 : test_app_filter_bus_get_allowlist (BusFixture *fixture,
822 : : gconstpointer test_data)
823 : : {
824 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
825 : 1 : g_autoptr(GError) local_error = NULL;
826 : 1 : const GetAppFilterData get_app_filter_data =
827 : : {
828 : 1 : .expected_uid = fixture->valid_uid,
829 : : .properties = "{"
830 : : "'AllowUserInstallation': <true>,"
831 : : "'AllowSystemInstallation': <true>,"
832 : : "'AppFilter': <(true, ["
833 : : "'app/org.gnome.Allowlisted1/x86_64/stable',"
834 : : "'app/org.gnome.Allowlisted2/x86_64/stable',"
835 : : "'/usr/bin/true',"
836 : : "'text/plain'"
837 : : "])>,"
838 : : "'OarsFilter': <('oars-1.1', @a{ss} {})>"
839 : : "}"
840 : : };
841 : :
842 : 1 : gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb,
843 : : (gpointer) &get_app_filter_data);
844 : :
845 : 1 : app_filter = mct_manager_get_app_filter (fixture->manager,
846 : : fixture->valid_uid,
847 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
848 : : &local_error);
849 : :
850 : 1 : g_assert_no_error (local_error);
851 : 1 : g_assert_nonnull (app_filter);
852 : :
853 : : /* Check the app filter properties. The returned filter is a allowlist,
854 : : * whereas typically a blocklist is returned. */
855 : 1 : g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid);
856 : 1 : g_assert_true (mct_app_filter_is_enabled (app_filter));
857 : 1 : g_assert_false (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Builder"));
858 : 1 : g_assert_true (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Allowlisted1"));
859 : 1 : g_assert_true (mct_app_filter_is_flatpak_app_allowed (app_filter, "org.gnome.Allowlisted2"));
860 : 1 : g_assert_true (mct_app_filter_is_flatpak_ref_allowed (app_filter, "app/org.gnome.Allowlisted1/x86_64/stable"));
861 : 1 : g_assert_false (mct_app_filter_is_flatpak_ref_allowed (app_filter, "app/org.gnome.Allowlisted1/x86_64/unknown"));
862 : 1 : g_assert_true (mct_app_filter_is_path_allowed (app_filter, "/usr/bin/true"));
863 : 1 : g_assert_false (mct_app_filter_is_path_allowed (app_filter, "/usr/bin/false"));
864 : 1 : g_assert_true (mct_app_filter_is_content_type_allowed (app_filter,
865 : : "text/plain"));
866 : 1 : g_assert_false (mct_app_filter_is_content_type_allowed (app_filter,
867 : : "x-scheme-handler/http"));
868 : 1 : }
869 : :
870 : : /* Test that getting an #MctAppFilter containing all possible OARS values from
871 : : * the mock D-Bus service works, and that the #MctAppFilter methods handle them
872 : : * correctly.
873 : : *
874 : : * The mock D-Bus replies are generated in get_app_filter_server_cb(). */
875 : : static void
876 : 1 : test_app_filter_bus_get_all_oars_values (BusFixture *fixture,
877 : : gconstpointer test_data)
878 : : {
879 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
880 : 1 : g_autoptr(GError) local_error = NULL;
881 : 1 : const GetAppFilterData get_app_filter_data =
882 : : {
883 : 1 : .expected_uid = fixture->valid_uid,
884 : : .properties = "{"
885 : : "'AllowUserInstallation': <true>,"
886 : : "'AllowSystemInstallation': <true>,"
887 : : "'AppFilter': <(false, @as [])>,"
888 : : "'OarsFilter': <('oars-1.1', {"
889 : : "'violence-bloodshed': 'none',"
890 : : "'violence-sexual': 'mild',"
891 : : "'violence-fantasy': 'moderate',"
892 : : "'violence-realistic': 'intense',"
893 : : "'language-profanity': 'other'"
894 : : "})>"
895 : : "}"
896 : : };
897 : :
898 : 1 : gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb,
899 : : (gpointer) &get_app_filter_data);
900 : :
901 : 1 : app_filter = mct_manager_get_app_filter (fixture->manager,
902 : : fixture->valid_uid,
903 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
904 : : &local_error);
905 : :
906 : 1 : g_assert_no_error (local_error);
907 : 1 : g_assert_nonnull (app_filter);
908 : :
909 : : /* Check the OARS filter properties. Each OARS value should have been parsed
910 : : * correctly, except for the unknown `other` one. */
911 : 1 : g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid);
912 : 1 : g_assert_true (mct_app_filter_is_enabled (app_filter));
913 : 1 : g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-bloodshed"), ==,
914 : : MCT_APP_FILTER_OARS_VALUE_NONE);
915 : 1 : g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-sexual"), ==,
916 : : MCT_APP_FILTER_OARS_VALUE_MILD);
917 : 1 : g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-fantasy"), ==,
918 : : MCT_APP_FILTER_OARS_VALUE_MODERATE);
919 : 1 : g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-realistic"), ==,
920 : : MCT_APP_FILTER_OARS_VALUE_INTENSE);
921 : 1 : g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "language-profanity"), ==,
922 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
923 : 1 : g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "unlisted-category"), ==,
924 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
925 : 1 : }
926 : :
927 : : /* Test that getting an #MctAppFilter containing only an `AppFilter` property
928 : : * from the mock D-Bus service works, and that the #MctAppFilter methods use
929 : : * appropriate defaults.
930 : : *
931 : : * The mock D-Bus replies are generated in get_app_filter_server_cb(). */
932 : : static void
933 : 1 : test_app_filter_bus_get_defaults (BusFixture *fixture,
934 : : gconstpointer test_data)
935 : : {
936 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
937 : 1 : g_autoptr(GError) local_error = NULL;
938 : 1 : const GetAppFilterData get_app_filter_data =
939 : : {
940 : 1 : .expected_uid = fixture->valid_uid,
941 : : .properties = "{"
942 : : "'AppFilter': <(false, @as [])>"
943 : : "}"
944 : : };
945 : 1 : g_autofree const gchar **oars_sections = NULL;
946 : :
947 : 1 : gt_dbus_queue_set_server_func (fixture->queue, get_app_filter_server_cb,
948 : : (gpointer) &get_app_filter_data);
949 : :
950 : 1 : app_filter = mct_manager_get_app_filter (fixture->manager,
951 : : fixture->valid_uid,
952 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
953 : : &local_error);
954 : :
955 : 1 : g_assert_no_error (local_error);
956 : 1 : g_assert_nonnull (app_filter);
957 : :
958 : : /* Check the default values for the properties. */
959 : 1 : g_assert_cmpuint (mct_app_filter_get_user_id (app_filter), ==, fixture->valid_uid);
960 : 1 : g_assert_false (mct_app_filter_is_enabled (app_filter));
961 : 1 : oars_sections = mct_app_filter_get_oars_sections (app_filter);
962 : 1 : g_assert_cmpuint (g_strv_length ((gchar **) oars_sections), ==, 0);
963 : 1 : g_assert_cmpint (mct_app_filter_get_oars_value (app_filter, "violence-bloodshed"), ==,
964 : : MCT_APP_FILTER_OARS_VALUE_UNKNOWN);
965 : 1 : g_assert_true (mct_app_filter_is_user_installation_allowed (app_filter));
966 : 1 : g_assert_false (mct_app_filter_is_system_installation_allowed (app_filter));
967 : 1 : }
968 : :
969 : : /* Test that mct_manager_get_app_filter() returns an appropriate error if the
970 : : * mock D-Bus service reports that the given user cannot be found.
971 : : *
972 : : * The mock D-Bus replies are generated inline. */
973 : : static void
974 : 1 : test_app_filter_bus_get_error_invalid_user (BusFixture *fixture,
975 : : gconstpointer test_data)
976 : : {
977 : 1 : g_autoptr(GAsyncResult) result = NULL;
978 : 1 : g_autoptr(GError) local_error = NULL;
979 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
980 : 1 : g_autofree gchar *error_message = NULL;
981 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
982 : :
983 : 1 : mct_manager_get_app_filter_async (fixture->manager,
984 : : fixture->missing_uid,
985 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
986 : : async_result_cb, &result);
987 : :
988 : : /* Handle the FindUserById() call and claim the user doesn’t exist. */
989 : : gint64 user_id;
990 : 1 : invocation =
991 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
992 : : "/org/freedesktop/Accounts",
993 : : "org.freedesktop.Accounts",
994 : : "FindUserById", "(x)", &user_id);
995 : 1 : g_assert_cmpint (user_id, ==, fixture->missing_uid);
996 : :
997 : 1 : error_message = g_strdup_printf ("Failed to look up user with uid %u.", fixture->missing_uid);
998 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
999 : : "org.freedesktop.Accounts.Error.Failed",
1000 : : error_message);
1001 : :
1002 : : /* Get the get_app_filter() result. */
1003 [ + + ]: 2 : while (result == NULL)
1004 : 1 : g_main_context_iteration (NULL, TRUE);
1005 : 1 : app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
1006 : : &local_error);
1007 : :
1008 : 1 : g_assert_error (local_error,
1009 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER);
1010 : 1 : g_assert_null (app_filter);
1011 : 1 : }
1012 : :
1013 : : /* Test that mct_manager_get_app_filter() returns an appropriate error if the
1014 : : * mock D-Bus service reports that the properties of the given user can’t be
1015 : : * accessed due to permissions.
1016 : : *
1017 : : * The mock D-Bus replies are generated inline. */
1018 : : static void
1019 : 1 : test_app_filter_bus_get_error_permission_denied (BusFixture *fixture,
1020 : : gconstpointer test_data)
1021 : : {
1022 : 1 : g_autoptr(GAsyncResult) result = NULL;
1023 : 1 : g_autoptr(GError) local_error = NULL;
1024 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
1025 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
1026 : 1 : g_autofree gchar *object_path = NULL;
1027 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
1028 : :
1029 : 1 : mct_manager_get_app_filter_async (fixture->manager,
1030 : : fixture->valid_uid,
1031 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1032 : : async_result_cb, &result);
1033 : :
1034 : : /* Handle the FindUserById() call. */
1035 : : gint64 user_id;
1036 : 1 : invocation1 =
1037 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1038 : : "/org/freedesktop/Accounts",
1039 : : "org.freedesktop.Accounts",
1040 : : "FindUserById", "(x)", &user_id);
1041 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1042 : :
1043 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1044 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
1045 : :
1046 : : /* Handle the Properties.GetAll() call and return a permission denied error. */
1047 : : const gchar *property_interface;
1048 : 1 : invocation2 =
1049 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1050 : : object_path,
1051 : : "org.freedesktop.DBus.Properties",
1052 : : "GetAll", "(&s)", &property_interface);
1053 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.AppFilter");
1054 : :
1055 : 1 : g_dbus_method_invocation_return_dbus_error (invocation2,
1056 : : "org.freedesktop.Accounts.Error.PermissionDenied",
1057 : : "Not authorized");
1058 : :
1059 : : /* Get the get_app_filter() result. */
1060 [ + + ]: 2 : while (result == NULL)
1061 : 1 : g_main_context_iteration (NULL, TRUE);
1062 : 1 : app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
1063 : : &local_error);
1064 : :
1065 : 1 : g_assert_error (local_error,
1066 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
1067 : 1 : g_assert_null (app_filter);
1068 : 1 : }
1069 : :
1070 : : /* Test that mct_manager_get_app_filter() returns an appropriate error if the
1071 : : * mock D-Bus service replies with no app filter properties (implying that it
1072 : : * hasn’t sent the property values because of permissions).
1073 : : *
1074 : : * The mock D-Bus replies are generated inline. */
1075 : : static void
1076 : 1 : test_app_filter_bus_get_error_permission_denied_missing (BusFixture *fixture,
1077 : : gconstpointer test_data)
1078 : : {
1079 : 1 : g_autoptr(GAsyncResult) result = NULL;
1080 : 1 : g_autoptr(GError) local_error = NULL;
1081 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
1082 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
1083 : 1 : g_autofree gchar *object_path = NULL;
1084 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
1085 : :
1086 : 1 : mct_manager_get_app_filter_async (fixture->manager,
1087 : : fixture->valid_uid,
1088 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1089 : : async_result_cb, &result);
1090 : :
1091 : : /* Handle the FindUserById() call. */
1092 : : gint64 user_id;
1093 : 1 : invocation1 =
1094 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1095 : : "/org/freedesktop/Accounts",
1096 : : "org.freedesktop.Accounts",
1097 : : "FindUserById", "(x)", &user_id);
1098 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1099 : :
1100 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1101 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
1102 : :
1103 : : /* Handle the Properties.GetAll() call and return an empty array due to not
1104 : : * having permission to access the properties. The code actually keys off the
1105 : : * presence of the AppFilter property, since that was the first one to be
1106 : : * added. */
1107 : : const gchar *property_interface;
1108 : 1 : invocation2 =
1109 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1110 : : object_path,
1111 : : "org.freedesktop.DBus.Properties",
1112 : : "GetAll", "(&s)", &property_interface);
1113 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.AppFilter");
1114 : :
1115 : 1 : g_dbus_method_invocation_return_value (invocation2, g_variant_new ("(a{sv})", NULL));
1116 : :
1117 : : /* Get the get_app_filter() result. */
1118 [ + + ]: 2 : while (result == NULL)
1119 : 1 : g_main_context_iteration (NULL, TRUE);
1120 : 1 : app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
1121 : : &local_error);
1122 : :
1123 : 1 : g_assert_error (local_error,
1124 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
1125 : 1 : g_assert_null (app_filter);
1126 : 1 : }
1127 : :
1128 : : /* Test that mct_manager_get_app_filter() returns an error if the mock D-Bus
1129 : : * service reports an unrecognised error.
1130 : : *
1131 : : * The mock D-Bus replies are generated inline. */
1132 : : static void
1133 : 1 : test_app_filter_bus_get_error_unknown (BusFixture *fixture,
1134 : : gconstpointer test_data)
1135 : : {
1136 : 1 : g_autoptr(GAsyncResult) result = NULL;
1137 : 1 : g_autoptr(GError) local_error = NULL;
1138 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
1139 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
1140 : :
1141 : 1 : mct_manager_get_app_filter_async (fixture->manager,
1142 : : fixture->valid_uid,
1143 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1144 : : async_result_cb, &result);
1145 : :
1146 : : /* Handle the FindUserById() call and return a bogus error. */
1147 : : gint64 user_id;
1148 : 1 : invocation =
1149 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1150 : : "/org/freedesktop/Accounts",
1151 : : "org.freedesktop.Accounts",
1152 : : "FindUserById", "(x)", &user_id);
1153 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1154 : :
1155 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
1156 : : "org.freedesktop.Accounts.Error.NewAndInterestingError",
1157 : : "This is a fake error message "
1158 : : "which libmalcontent "
1159 : : "will never have seen before, "
1160 : : "but must still handle correctly");
1161 : :
1162 : : /* Get the get_app_filter() result. */
1163 [ + + ]: 2 : while (result == NULL)
1164 : 1 : g_main_context_iteration (NULL, TRUE);
1165 : 1 : app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
1166 : : &local_error);
1167 : :
1168 : : /* We don’t actually care what error is actually used here. */
1169 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
1170 : 1 : g_assert_null (app_filter);
1171 : 1 : }
1172 : :
1173 : : /* Test that mct_manager_get_app_filter() returns an error if the mock D-Bus
1174 : : * service reports an unknown interface, which means that parental controls are
1175 : : * not installed properly.
1176 : : *
1177 : : * The mock D-Bus replies are generated inline. */
1178 : : static void
1179 : 1 : test_app_filter_bus_get_error_disabled (BusFixture *fixture,
1180 : : gconstpointer test_data)
1181 : : {
1182 : 1 : g_autoptr(GAsyncResult) result = NULL;
1183 : 1 : g_autoptr(GError) local_error = NULL;
1184 : 1 : g_autoptr(GDBusMethodInvocation) invocation1 = NULL;
1185 : 1 : g_autoptr(GDBusMethodInvocation) invocation2 = NULL;
1186 : 1 : g_autofree gchar *object_path = NULL;
1187 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
1188 : :
1189 : 1 : mct_manager_get_app_filter_async (fixture->manager,
1190 : : fixture->valid_uid,
1191 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE, NULL,
1192 : : async_result_cb, &result);
1193 : :
1194 : : /* Handle the FindUserById() call. */
1195 : : gint64 user_id;
1196 : 1 : invocation1 =
1197 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1198 : : "/org/freedesktop/Accounts",
1199 : : "org.freedesktop.Accounts",
1200 : : "FindUserById", "(x)", &user_id);
1201 : 1 : g_assert_cmpint (user_id, ==, fixture->valid_uid);
1202 : :
1203 : 1 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1204 : 1 : g_dbus_method_invocation_return_value (invocation1, g_variant_new ("(o)", object_path));
1205 : :
1206 : : /* Handle the Properties.GetAll() call and return an InvalidArgs error. */
1207 : : const gchar *property_interface;
1208 : 1 : invocation2 =
1209 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1210 : : object_path,
1211 : : "org.freedesktop.DBus.Properties",
1212 : : "GetAll", "(&s)", &property_interface);
1213 : 1 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.AppFilter");
1214 : :
1215 : 1 : g_dbus_method_invocation_return_dbus_error (invocation2,
1216 : : "org.freedesktop.DBus.Error.InvalidArgs",
1217 : : "No such interface "
1218 : : "“com.endlessm.ParentalControls.AppFilter”");
1219 : :
1220 : : /* Get the get_app_filter() result. */
1221 [ + + ]: 2 : while (result == NULL)
1222 : 1 : g_main_context_iteration (NULL, TRUE);
1223 : 1 : app_filter = mct_manager_get_app_filter_finish (fixture->manager, result,
1224 : : &local_error);
1225 : :
1226 : 1 : g_assert_error (local_error,
1227 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_DISABLED);
1228 : 1 : g_assert_null (app_filter);
1229 : 1 : }
1230 : :
1231 : : /* Generic mock accountsservice implementation which handles properties being
1232 : : * set on a mock User object, and compares their values to the given
1233 : : * `expected_*` ones.
1234 : : *
1235 : : * If @error_index is non-negative, it gives the index of a Set() call to return
1236 : : * the given @dbus_error_name and @dbus_error_message from, rather than
1237 : : * accepting the property value from the caller. If @error_index is negative,
1238 : : * all Set() calls will be accepted. */
1239 : : typedef struct
1240 : : {
1241 : : uid_t expected_uid;
1242 : :
1243 : : /* All GVariants in text format: */
1244 : : const gchar *expected_app_filter_value; /* (nullable) */
1245 : : const gchar *expected_oars_filter_value; /* (nullable) */
1246 : : const gchar *expected_allow_user_installation_value; /* (nullable) */
1247 : : const gchar *expected_allow_system_installation_value; /* (nullable) */
1248 : :
1249 : : gint error_index; /* -1 to return no error */
1250 : : const gchar *dbus_error_name; /* NULL to return no error */
1251 : : const gchar *dbus_error_message; /* NULL to return no error */
1252 : : } SetAppFilterData;
1253 : :
1254 : : static const gchar *
1255 : 14 : set_app_filter_data_get_expected_property_value (const SetAppFilterData *data,
1256 : : const gchar *property_name)
1257 : : {
1258 [ + + ]: 14 : if (g_str_equal (property_name, "AppFilter"))
1259 : 5 : return data->expected_app_filter_value;
1260 [ + + ]: 9 : else if (g_str_equal (property_name, "OarsFilter"))
1261 : 4 : return data->expected_oars_filter_value;
1262 [ + + ]: 5 : else if (g_str_equal (property_name, "AllowUserInstallation"))
1263 : 3 : return data->expected_allow_user_installation_value;
1264 [ + - ]: 2 : else if (g_str_equal (property_name, "AllowSystemInstallation"))
1265 : 2 : return data->expected_allow_system_installation_value;
1266 : : else
1267 : : g_assert_not_reached ();
1268 : : }
1269 : :
1270 : : /* This is run in a worker thread. */
1271 : : static void
1272 : 8 : set_app_filter_server_cb (GtDBusQueue *queue,
1273 : : gpointer user_data)
1274 : : {
1275 : 8 : const SetAppFilterData *data = user_data;
1276 : 8 : g_autoptr(GDBusMethodInvocation) find_invocation = NULL;
1277 : 8 : g_autofree gchar *object_path = NULL;
1278 : :
1279 : 8 : g_assert ((data->error_index == -1) == (data->dbus_error_name == NULL));
1280 : 8 : g_assert ((data->dbus_error_name == NULL) == (data->dbus_error_message == NULL));
1281 : :
1282 : : /* Handle the FindUserById() call. */
1283 : : gint64 user_id;
1284 : 8 : find_invocation =
1285 : 8 : gt_dbus_queue_assert_pop_message (queue,
1286 : : "/org/freedesktop/Accounts",
1287 : : "org.freedesktop.Accounts",
1288 : : "FindUserById", "(x)", &user_id);
1289 : 8 : g_assert_cmpint (user_id, ==, data->expected_uid);
1290 : :
1291 : 8 : object_path = g_strdup_printf ("/org/freedesktop/Accounts/User%u", (uid_t) user_id);
1292 : 8 : g_dbus_method_invocation_return_value (find_invocation, g_variant_new ("(o)", object_path));
1293 : :
1294 : : /* Handle the Properties.Set() calls. */
1295 : 8 : const gchar *expected_properties[] =
1296 : : {
1297 : : "AppFilter",
1298 : : "OarsFilter",
1299 : : "AllowUserInstallation",
1300 : : "AllowSystemInstallation",
1301 : : };
1302 : : gsize i;
1303 : :
1304 [ + + ]: 22 : for (i = 0; i < G_N_ELEMENTS (expected_properties); i++)
1305 : : {
1306 : : const gchar *property_interface;
1307 : : const gchar *property_name;
1308 [ + + ]: 20 : g_autoptr(GVariant) property_value = NULL;
1309 [ + + ]: 20 : g_autoptr(GDBusMethodInvocation) property_invocation = NULL;
1310 [ + + ]: 20 : g_autoptr(GVariant) expected_property_value = NULL;
1311 : :
1312 : 20 : property_invocation =
1313 : 20 : gt_dbus_queue_assert_pop_message (queue,
1314 : : object_path,
1315 : : "org.freedesktop.DBus.Properties",
1316 : : "Set", "(&s&sv)", &property_interface,
1317 : : &property_name, &property_value);
1318 : 20 : g_assert_cmpstr (property_interface, ==, "com.endlessm.ParentalControls.AppFilter");
1319 : 20 : g_assert_cmpstr (property_name, ==, expected_properties[i]);
1320 : :
1321 [ + + + + ]: 20 : if (data->error_index >= 0 && (gsize) data->error_index == i)
1322 : : {
1323 : 6 : g_dbus_method_invocation_return_dbus_error (property_invocation,
1324 : 6 : data->dbus_error_name,
1325 : 6 : data->dbus_error_message);
1326 : 6 : break;
1327 : : }
1328 : : else
1329 : : {
1330 : 14 : expected_property_value = g_variant_new_parsed (set_app_filter_data_get_expected_property_value (data, property_name));
1331 : 14 : g_assert_cmpvariant (property_value, expected_property_value);
1332 : :
1333 : 14 : g_dbus_method_invocation_return_value (property_invocation, NULL);
1334 : : }
1335 : : }
1336 : 8 : }
1337 : :
1338 : : /* Test that setting an #MctAppFilter on the mock D-Bus service works. The
1339 : : * @test_data is a boolean value indicating whether to do the call
1340 : : * synchronously (%FALSE) or asynchronously (%TRUE).
1341 : : *
1342 : : * The mock D-Bus replies are generated in set_app_filter_server_cb(), which is
1343 : : * used for both synchronous and asynchronous calls. */
1344 : : static void
1345 : 2 : test_app_filter_bus_set (BusFixture *fixture,
1346 : : gconstpointer test_data)
1347 : : {
1348 : : gboolean success;
1349 : 4 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
1350 : 2 : g_autoptr(MctAppFilter) app_filter = NULL;
1351 : 2 : g_autoptr(GError) local_error = NULL;
1352 : 2 : gboolean test_async = GPOINTER_TO_UINT (test_data);
1353 : 2 : const SetAppFilterData set_app_filter_data =
1354 : : {
1355 : 2 : .expected_uid = fixture->valid_uid,
1356 : : .expected_app_filter_value = "(false, ['/usr/bin/false', '/usr/bin/banned', 'app/org.gnome.Nasty/x86_64/stable', 'x-scheme-handler/http'])",
1357 : : .expected_oars_filter_value = "('oars-1.1', { 'violence-fantasy': 'intense' })",
1358 : : .expected_allow_user_installation_value = "true",
1359 : : .expected_allow_system_installation_value = "true",
1360 : : .error_index = -1,
1361 : : };
1362 : :
1363 : : /* Build an app filter. */
1364 : 2 : mct_app_filter_builder_blocklist_path (&builder, "/usr/bin/false");
1365 : 2 : mct_app_filter_builder_blocklist_path (&builder, "/usr/bin/banned");
1366 : 2 : mct_app_filter_builder_blocklist_flatpak_ref (&builder, "app/org.gnome.Nasty/x86_64/stable");
1367 : 2 : mct_app_filter_builder_blocklist_content_type (&builder, "x-scheme-handler/http");
1368 : 2 : mct_app_filter_builder_set_oars_value (&builder, "violence-fantasy", MCT_APP_FILTER_OARS_VALUE_INTENSE);
1369 : 2 : mct_app_filter_builder_set_allow_user_installation (&builder, TRUE);
1370 : 2 : mct_app_filter_builder_set_allow_system_installation (&builder, TRUE);
1371 : :
1372 : 2 : app_filter = mct_app_filter_builder_end (&builder);
1373 : :
1374 : : /* Set the mock service function and set the filter. */
1375 : 2 : gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb,
1376 : : (gpointer) &set_app_filter_data);
1377 : :
1378 [ + + ]: 2 : if (test_async)
1379 : : {
1380 : 1 : g_autoptr(GAsyncResult) result = NULL;
1381 : :
1382 : 1 : mct_manager_set_app_filter_async (fixture->manager,
1383 : : fixture->valid_uid, app_filter,
1384 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1385 : : async_result_cb, &result);
1386 : :
1387 [ + + ]: 5 : while (result == NULL)
1388 : 4 : g_main_context_iteration (NULL, TRUE);
1389 : 1 : success = mct_manager_set_app_filter_finish (fixture->manager, result,
1390 : : &local_error);
1391 : : }
1392 : : else
1393 : : {
1394 : 1 : success = mct_manager_set_app_filter (fixture->manager,
1395 : : fixture->valid_uid, app_filter,
1396 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1397 : : &local_error);
1398 : : }
1399 : :
1400 : 2 : g_assert_no_error (local_error);
1401 : 2 : g_assert_true (success);
1402 : 2 : }
1403 : :
1404 : : /* Test that mct_manager__set_app_filter() returns an appropriate error if the
1405 : : * mock D-Bus service reports that the given user cannot be found.
1406 : : *
1407 : : * The mock D-Bus replies are generated inline. */
1408 : : static void
1409 : 1 : test_app_filter_bus_set_error_invalid_user (BusFixture *fixture,
1410 : : gconstpointer test_data)
1411 : : {
1412 : : gboolean success;
1413 : 2 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
1414 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
1415 : 1 : g_autoptr(GAsyncResult) result = NULL;
1416 : 1 : g_autoptr(GError) local_error = NULL;
1417 : 1 : g_autoptr(GDBusMethodInvocation) invocation = NULL;
1418 : 1 : g_autofree gchar *error_message = NULL;
1419 : :
1420 : : /* Use the default app filter. */
1421 : 1 : app_filter = mct_app_filter_builder_end (&builder);
1422 : :
1423 : 1 : mct_manager_set_app_filter_async (fixture->manager,
1424 : : fixture->missing_uid, app_filter,
1425 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1426 : : async_result_cb, &result);
1427 : :
1428 : : /* Handle the FindUserById() call and claim the user doesn’t exist. */
1429 : : gint64 user_id;
1430 : 1 : invocation =
1431 : 1 : gt_dbus_queue_assert_pop_message (fixture->queue,
1432 : : "/org/freedesktop/Accounts",
1433 : : "org.freedesktop.Accounts",
1434 : : "FindUserById", "(x)", &user_id);
1435 : 1 : g_assert_cmpint (user_id, ==, fixture->missing_uid);
1436 : :
1437 : 1 : error_message = g_strdup_printf ("Failed to look up user with uid %u.", fixture->missing_uid);
1438 : 1 : g_dbus_method_invocation_return_dbus_error (invocation,
1439 : : "org.freedesktop.Accounts.Error.Failed",
1440 : : error_message);
1441 : :
1442 : : /* Get the set_app_filter() result. */
1443 [ + + ]: 2 : while (result == NULL)
1444 : 1 : g_main_context_iteration (NULL, TRUE);
1445 : 1 : success = mct_manager_set_app_filter_finish (fixture->manager, result,
1446 : : &local_error);
1447 : :
1448 : 1 : g_assert_error (local_error,
1449 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_INVALID_USER);
1450 : 1 : g_assert_false (success);
1451 : 1 : }
1452 : :
1453 : : /* Test that mct_manager_set_app_filter() returns an appropriate error if the
1454 : : * mock D-Bus service replies with a permission denied error when setting
1455 : : * properties.
1456 : : *
1457 : : * The mock D-Bus replies are generated in set_app_filter_server_cb(). */
1458 : : static void
1459 : 1 : test_app_filter_bus_set_error_permission_denied (BusFixture *fixture,
1460 : : gconstpointer test_data)
1461 : : {
1462 : : gboolean success;
1463 : 2 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
1464 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
1465 : 1 : g_autoptr(GError) local_error = NULL;
1466 : 1 : const SetAppFilterData set_app_filter_data =
1467 : : {
1468 : 1 : .expected_uid = fixture->valid_uid,
1469 : : .error_index = 0,
1470 : : .dbus_error_name = "org.freedesktop.Accounts.Error.PermissionDenied",
1471 : : .dbus_error_message = "Not authorized",
1472 : : };
1473 : :
1474 : : /* Use the default app filter. */
1475 : 1 : app_filter = mct_app_filter_builder_end (&builder);
1476 : :
1477 : 1 : gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb,
1478 : : (gpointer) &set_app_filter_data);
1479 : :
1480 : 1 : success = mct_manager_set_app_filter (fixture->manager,
1481 : : fixture->valid_uid, app_filter,
1482 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1483 : : &local_error);
1484 : :
1485 : 1 : g_assert_error (local_error,
1486 : : MCT_MANAGER_ERROR, MCT_MANAGER_ERROR_PERMISSION_DENIED);
1487 : 1 : g_assert_false (success);
1488 : 1 : }
1489 : :
1490 : : /* Test that mct_manager_set_app_filter() returns an error if the mock D-Bus
1491 : : * service reports an unrecognised error.
1492 : : *
1493 : : * The mock D-Bus replies are generated in set_app_filter_server_cb(). */
1494 : : static void
1495 : 1 : test_app_filter_bus_set_error_unknown (BusFixture *fixture,
1496 : : gconstpointer test_data)
1497 : : {
1498 : : gboolean success;
1499 : 2 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
1500 : 1 : g_autoptr(MctAppFilter) app_filter = NULL;
1501 : 1 : g_autoptr(GError) local_error = NULL;
1502 : 1 : const SetAppFilterData set_app_filter_data =
1503 : : {
1504 : 1 : .expected_uid = fixture->valid_uid,
1505 : : .error_index = 0,
1506 : : .dbus_error_name = "org.freedesktop.Accounts.Error.NewAndInterestingError",
1507 : : .dbus_error_message = "This is a fake error message which "
1508 : : "libmalcontent will never have seen "
1509 : : "before, but must still handle correctly",
1510 : : };
1511 : :
1512 : : /* Use the default app filter. */
1513 : 1 : app_filter = mct_app_filter_builder_end (&builder);
1514 : :
1515 : 1 : gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb,
1516 : : (gpointer) &set_app_filter_data);
1517 : :
1518 : 1 : success = mct_manager_set_app_filter (fixture->manager,
1519 : : fixture->valid_uid, app_filter,
1520 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1521 : : &local_error);
1522 : :
1523 : 1 : g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_DBUS_ERROR);
1524 : 1 : g_assert_false (success);
1525 : 1 : }
1526 : :
1527 : : /* Test that mct_manager_set_app_filter() returns an error if the mock D-Bus
1528 : : * service reports an InvalidArgs error with a given one of its Set() calls.
1529 : : *
1530 : : * @test_data contains a property index encoded with GINT_TO_POINTER(),
1531 : : * indicating which Set() call to return the error on, since the calls are made
1532 : : * in series.
1533 : : *
1534 : : * The mock D-Bus replies are generated in set_app_filter_server_cb(). */
1535 : : static void
1536 : 4 : test_app_filter_bus_set_error_invalid_property (BusFixture *fixture,
1537 : : gconstpointer test_data)
1538 : : {
1539 : : gboolean success;
1540 : 8 : g_auto(MctAppFilterBuilder) builder = MCT_APP_FILTER_BUILDER_INIT ();
1541 : 4 : g_autoptr(MctAppFilter) app_filter = NULL;
1542 : 4 : g_autoptr(GError) local_error = NULL;
1543 : 4 : const SetAppFilterData set_app_filter_data =
1544 : : {
1545 : 4 : .expected_uid = fixture->valid_uid,
1546 : : .expected_app_filter_value = "(false, @as [])",
1547 : : .expected_oars_filter_value = "('oars-1.1', @a{ss} [])",
1548 : : .expected_allow_user_installation_value = "true",
1549 : : .expected_allow_system_installation_value = "false",
1550 : 4 : .error_index = GPOINTER_TO_INT (test_data),
1551 : : .dbus_error_name = "org.freedesktop.DBus.Error.InvalidArgs",
1552 : : .dbus_error_message = "Mumble mumble something wrong with the filter value",
1553 : : };
1554 : :
1555 : : /* Use the default app filter. */
1556 : 4 : app_filter = mct_app_filter_builder_end (&builder);
1557 : :
1558 : 4 : gt_dbus_queue_set_server_func (fixture->queue, set_app_filter_server_cb,
1559 : : (gpointer) &set_app_filter_data);
1560 : :
1561 : 4 : success = mct_manager_set_app_filter (fixture->manager,
1562 : : fixture->valid_uid, app_filter,
1563 : : MCT_MANAGER_SET_VALUE_FLAGS_NONE, NULL,
1564 : : &local_error);
1565 : :
1566 : 4 : g_assert_error (local_error, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS);
1567 : 4 : g_assert_false (success);
1568 : 4 : }
1569 : :
1570 : : int
1571 : 2 : main (int argc,
1572 : : char **argv)
1573 : : {
1574 : 2 : setlocale (LC_ALL, "");
1575 : 2 : g_test_init (&argc, &argv, NULL);
1576 : :
1577 : 2 : g_test_add_func ("/app-filter/error-quark", test_app_filter_error_quark);
1578 : 2 : g_test_add_func ("/app-filter/types", test_app_filter_types);
1579 : 2 : g_test_add_func ("/app-filter/refs", test_app_filter_refs);
1580 : :
1581 : 2 : g_test_add_func ("/app-filter/serialize", test_app_filter_serialize);
1582 : 2 : g_test_add_func ("/app-filter/deserialize", test_app_filter_deserialize);
1583 : 2 : g_test_add_func ("/app-filter/deserialize/invalid", test_app_filter_deserialize_invalid);
1584 : :
1585 : 2 : g_test_add_func ("/app-filter/equal", test_app_filter_equal);
1586 : :
1587 : 2 : g_test_add_func ("/app-filter/is-enabled", test_app_filter_is_enabled);
1588 : :
1589 : 2 : g_test_add ("/app-filter/builder/stack/non-empty", BuilderFixture, NULL,
1590 : : builder_set_up_stack, test_app_filter_builder_non_empty,
1591 : : builder_tear_down_stack);
1592 : 2 : g_test_add ("/app-filter/builder/stack/empty", BuilderFixture, NULL,
1593 : : builder_set_up_stack, test_app_filter_builder_empty,
1594 : : builder_tear_down_stack);
1595 : 2 : g_test_add ("/app-filter/builder/stack2/non-empty", BuilderFixture, NULL,
1596 : : builder_set_up_stack2, test_app_filter_builder_non_empty,
1597 : : builder_tear_down_stack2);
1598 : 2 : g_test_add ("/app-filter/builder/stack2/empty", BuilderFixture, NULL,
1599 : : builder_set_up_stack2, test_app_filter_builder_empty,
1600 : : builder_tear_down_stack2);
1601 : 2 : g_test_add ("/app-filter/builder/heap/non-empty", BuilderFixture, NULL,
1602 : : builder_set_up_heap, test_app_filter_builder_non_empty,
1603 : : builder_tear_down_heap);
1604 : 2 : g_test_add ("/app-filter/builder/heap/empty", BuilderFixture, NULL,
1605 : : builder_set_up_heap, test_app_filter_builder_empty,
1606 : : builder_tear_down_heap);
1607 : 2 : g_test_add_func ("/app-filter/builder/copy/empty",
1608 : : test_app_filter_builder_copy_empty);
1609 : 2 : g_test_add_func ("/app-filter/builder/copy/full",
1610 : : test_app_filter_builder_copy_full);
1611 : :
1612 : 2 : g_test_add_func ("/app-filter/appinfo", test_app_filter_appinfo);
1613 : :
1614 : 2 : g_test_add ("/app-filter/bus/get/async", BusFixture, GUINT_TO_POINTER (TRUE),
1615 : : bus_set_up, test_app_filter_bus_get, bus_tear_down);
1616 : 2 : g_test_add ("/app-filter/bus/get/sync", BusFixture, GUINT_TO_POINTER (FALSE),
1617 : : bus_set_up, test_app_filter_bus_get, bus_tear_down);
1618 : 2 : g_test_add ("/app-filter/bus/get/allowlist", BusFixture, NULL,
1619 : : bus_set_up, test_app_filter_bus_get_allowlist, bus_tear_down);
1620 : 2 : g_test_add ("/app-filter/bus/get/all-oars-values", BusFixture, NULL,
1621 : : bus_set_up, test_app_filter_bus_get_all_oars_values, bus_tear_down);
1622 : 2 : g_test_add ("/app-filter/bus/get/defaults", BusFixture, NULL,
1623 : : bus_set_up, test_app_filter_bus_get_defaults, bus_tear_down);
1624 : :
1625 : 2 : g_test_add ("/app-filter/bus/get/error/invalid-user", BusFixture, NULL,
1626 : : bus_set_up, test_app_filter_bus_get_error_invalid_user, bus_tear_down);
1627 : 2 : g_test_add ("/app-filter/bus/get/error/permission-denied", BusFixture, NULL,
1628 : : bus_set_up, test_app_filter_bus_get_error_permission_denied, bus_tear_down);
1629 : 2 : g_test_add ("/app-filter/bus/get/error/permission-denied-missing", BusFixture, NULL,
1630 : : bus_set_up, test_app_filter_bus_get_error_permission_denied_missing, bus_tear_down);
1631 : 2 : g_test_add ("/app-filter/bus/get/error/unknown", BusFixture, NULL,
1632 : : bus_set_up, test_app_filter_bus_get_error_unknown, bus_tear_down);
1633 : 2 : g_test_add ("/app-filter/bus/get/error/disabled", BusFixture, NULL,
1634 : : bus_set_up, test_app_filter_bus_get_error_disabled, bus_tear_down);
1635 : :
1636 : 2 : g_test_add ("/app-filter/bus/set/async", BusFixture, GUINT_TO_POINTER (TRUE),
1637 : : bus_set_up, test_app_filter_bus_set, bus_tear_down);
1638 : 2 : g_test_add ("/app-filter/bus/set/sync", BusFixture, GUINT_TO_POINTER (FALSE),
1639 : : bus_set_up, test_app_filter_bus_set, bus_tear_down);
1640 : :
1641 : 2 : g_test_add ("/app-filter/bus/set/error/invalid-user", BusFixture, NULL,
1642 : : bus_set_up, test_app_filter_bus_set_error_invalid_user, bus_tear_down);
1643 : 2 : g_test_add ("/app-filter/bus/set/error/permission-denied", BusFixture, NULL,
1644 : : bus_set_up, test_app_filter_bus_set_error_permission_denied, bus_tear_down);
1645 : 2 : g_test_add ("/app-filter/bus/set/error/unknown", BusFixture, NULL,
1646 : : bus_set_up, test_app_filter_bus_set_error_unknown, bus_tear_down);
1647 : 2 : g_test_add ("/app-filter/bus/set/error/invalid-property/app-filter",
1648 : : BusFixture, GINT_TO_POINTER (0), bus_set_up,
1649 : : test_app_filter_bus_set_error_invalid_property, bus_tear_down);
1650 : 2 : g_test_add ("/app-filter/bus/set/error/invalid-property/oars-filter",
1651 : : BusFixture, GINT_TO_POINTER (1), bus_set_up,
1652 : : test_app_filter_bus_set_error_invalid_property, bus_tear_down);
1653 : 2 : g_test_add ("/app-filter/bus/set/error/invalid-property/allow-user-installation",
1654 : : BusFixture, GINT_TO_POINTER (2), bus_set_up,
1655 : : test_app_filter_bus_set_error_invalid_property, bus_tear_down);
1656 : 2 : g_test_add ("/app-filter/bus/set/error/invalid-property/allow-system-installation",
1657 : : BusFixture, GINT_TO_POINTER (3), bus_set_up,
1658 : : test_app_filter_bus_set_error_invalid_property, bus_tear_down);
1659 : :
1660 : 2 : return g_test_run ();
1661 : : }
|