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