context: Add new FlatpakPermission(s) type

This adds a new type that is meant to track more complex permissions
than a pure bitmask, including conditional dependencies. It is not yet
used, but it will be used for at least the socket and device
permissions.

For each possible permission we track whether the permission is
unconditionally allowed, unconditionally disallowed, or if it is
conditionally allowed (allowed if some conditions are met).

Additionally we track for each permission whether stacking the context
on top of another will reset permissions in the layer below. This is a
new feature, because previously merging layers *always* overrode the
value from below, whereas conditional permissions can either stack on
top of, or replace the underlying layer.

In terms of the keyfile, there are 4 possible types of layers:

1) Add a permission, removes all partial permissions below
socket=pipewire

2) Remove access, removes both partial and full permissions below
socket=!pipewire

3) Adds a partial permission, keeping whatever is already there:
socket=pipewire;pipewire:if:has-wayland
Note: This adds a plain `pipewire` for backwards compat.
Note: If parent has full pipewire access, this is a no-op.

4) Adds a partial permission, remove all previous access
socket=!pipewire;pipewire;pipewire:if:has-wayland
Note: This seems weird as it has both !pipewire and pipewire, but older versions
will read these in order and get the right result.

Additionally, partial permissions can have multiple conditions:
socket=pipewire;pipewire:if:has-something;pipewire:if:has-other;

In such a case the socket will be accessible if any condition matches.

Conditions can also be negated:
socket=pipewire;pipewire:if:!has-something;

Due to backwards compatibility we have to add the non-conditional
permission as well as the conditional, as older flatpak will ignore
the conditional. This is handle when serializing/deserializing the
permissions, and internally we don't have to care about this.
This commit is contained in:
Alexander Larsson
2025-07-04 13:16:04 +02:00
committed by Sebastian Wick
parent 5852e6f5e6
commit b93b58a44e
2 changed files with 846 additions and 0 deletions

View File

@@ -80,6 +80,13 @@ typedef enum {
FLATPAK_CONTEXT_FEATURE_PER_APP_DEV_SHM = 1 << 4,
} FlatpakContextFeatures;
typedef enum {
FLATPAK_CONTEXT_CONDITION_TRUE = 1 << 0,
FLATPAK_CONTEXT_CONDITION_FALSE = 1 << 1,
FLATPAK_CONTEXT_CONDITION_HAS_INPUT_DEV = 1 << 2,
FLATPAK_CONTEXT_CONDITION_HAS_WAYLAND = 1 << 3,
} FlatpakContextConditions;
struct FlatpakContext
{
FlatpakContextShares shares;
@@ -101,6 +108,9 @@ struct FlatpakContext
GHashTable *hidden_usb_devices;
};
/* Gets a single condition as param and returns whether the condition is true. */
typedef gboolean (*FlatpakContextConditionEvaluator) (FlatpakContextConditions condition);
extern const char *flatpak_context_sockets[];
extern const char *flatpak_context_devices[];
extern const char *flatpak_context_features[];

View File

@@ -98,6 +98,842 @@ const char *flatpak_context_special_filesystems[] = {
NULL
};
const char *flatpak_context_conditions[] = {
"true",
"false",
"has-input-device",
"has-wayland",
NULL
};
FlatpakContextConditions flatpak_context_true_conditions =
FLATPAK_CONTEXT_CONDITION_TRUE |
FLATPAK_CONTEXT_CONDITION_HAS_INPUT_DEV;
static const char *parse_negated (const char *option, gboolean *negated);
static guint32 flatpak_context_bitmask_from_string (const char *name, const char **names);
typedef struct FlatpakPermission FlatpakPermission;
struct FlatpakPermission {
/* Is the permission unconditionally allowed */
gboolean allowed;
/* When layering, reset all permissions below */
gboolean reset;
/* Assumes allowed is false */
GPtrArray *conditionals;
/* Only used during deserialization */
gboolean disallow_if_conditional;
gboolean disallow_if_conditional_original_reset;
GPtrArray *disallow_if_conditional_original_conditionals;
};
static FlatpakPermission *
flatpak_permission_new (void)
{
FlatpakPermission *permission;
permission = g_slice_new0 (FlatpakPermission);
permission->conditionals = g_ptr_array_new_with_free_func (g_free);
permission->disallow_if_conditional_original_conditionals =
g_ptr_array_new_with_free_func (g_free);
return permission;
};
static void
flatpak_permission_free (FlatpakPermission *permission)
{
g_ptr_array_free (permission->conditionals, TRUE);
g_ptr_array_free (permission->disallow_if_conditional_original_conditionals,
TRUE);
g_slice_free (FlatpakPermission, permission);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakPermission, flatpak_permission_free)
static FlatpakPermission *
flatpak_permission_dup (FlatpakPermission *permission)
{
FlatpakPermission *copy = NULL;
copy = flatpak_permission_new ();
copy->allowed = permission->allowed;
copy->reset = permission->reset;
for (size_t i = 0; i < permission->conditionals->len; i++) {
const char *condition = permission->conditionals->pdata[i];
g_ptr_array_add (copy->conditionals, g_strdup (condition));
}
return copy;
}
static void
flatpak_permission_set_not_allowed (FlatpakPermission *permission)
{
permission->allowed = FALSE;
permission->reset = TRUE;
g_ptr_array_set_size (permission->conditionals, 0);
}
static void
flatpak_permission_set_allowed (FlatpakPermission *permission)
{
permission->allowed = TRUE;
/* We reset even when allowed, because lower layer conditionals being added
* at merge would make this non-conditional layer conditional. */
permission->reset = TRUE;
g_ptr_array_set_size (permission->conditionals, 0);
}
static void
flatpak_permission_set_allowed_if (FlatpakPermission *permission,
const char *condition)
{
/* If we are already unconditionally allowed, don't add useless conditionals */
if (permission->allowed)
return;
/* Check if its already there */
if (g_ptr_array_find_with_equal_func (permission->conditionals,
condition,
g_str_equal, NULL))
return;
g_ptr_array_add (permission->conditionals, g_strdup (condition));
g_ptr_array_sort (permission->conditionals, flatpak_strcmp0_ptr);
}
static void
flatpak_permission_serialize (FlatpakPermission *permission,
const char *name,
GPtrArray *res,
gboolean flatten)
{
if (permission->allowed)
{
/* Completely allowed */
g_ptr_array_add (res, g_strdup (name));
g_assert (permission->conditionals->len == 0);
/* A non-conditional add always implies reset, so no need to serialize that */
}
else if (permission->conditionals->len > 0)
{
/* Partially allowed */
if (permission->reset && !flatten)
g_ptr_array_add (res, g_strdup_printf ("!%s", name));
/* As backwards compat for pre-conditional flatpaks we unconditionally
* add this first. New versions will ignore this if there are
* any conditionals.
* Note: This may result in both "!foo" and "foo", but that
* is fine as the "foo" is last and wins for older flatpaks.
*/
g_ptr_array_add (res, g_strdup (name));
for (size_t i = 0; i < permission->conditionals->len; i++)
{
const char *conditional = permission->conditionals->pdata[i];
g_ptr_array_add (res, g_strdup_printf ("if:%s:%s", name, conditional));
}
}
else
{
/* Completely disallowed */
if (!flatten)
g_ptr_array_add (res, g_strdup_printf ("!%s", name));
}
}
static void
flatpak_permission_to_args (FlatpakPermission *permission,
const char *argname,
const char *name,
GPtrArray *args)
{
if (permission->allowed)
{
/* Completely allowed */
g_ptr_array_add (args, g_strdup_printf ("--%s=%s", argname, name));
}
else if (permission->conditionals->len > 0)
{
/* Partially allowed */
if (permission->reset)
g_ptr_array_add (args, g_strdup_printf ("--no%s=%s", argname, name));
for (size_t i = 0; i < permission->conditionals->len; i++)
{
const char *conditional = permission->conditionals->pdata[i];
g_ptr_array_add (args, g_strdup_printf ("--%s-if=%s:%s",
argname, name, conditional));
}
}
else
{
/* Completely disallowed */
g_ptr_array_add (args, g_strdup_printf ("--no%s=%s", argname, name));
}
}
static void
flatpak_permission_deserialize (FlatpakPermission *permission,
gboolean negated,
const char *maybe_condition)
{
/* This can't use the flatpak_permission_set_ helpers, because we
* have to be wary of the backward compat non-conditional permission
* in case conditionals are used. */
if (maybe_condition == NULL)
{
/* Non-conditional option, these are always before conditionals,
* but if non-negated could be backwards compat for later conditional. */
if (negated)
{
permission->allowed = FALSE;
permission->reset = TRUE;
}
else
{
GPtrArray *tmp;
/* Allow us to revert this if it is a backwards compat */
permission->disallow_if_conditional = TRUE;
permission->disallow_if_conditional_original_reset = permission->reset;
tmp = permission->conditionals;
permission->conditionals =
permission->disallow_if_conditional_original_conditionals;
permission->disallow_if_conditional_original_conditionals = tmp;
permission->allowed = TRUE;
permission->reset = TRUE;
}
}
else
{
/* Conditional option */
if (permission->disallow_if_conditional)
{
GPtrArray *tmp;
/* Previous allow was a backward compat, revert it */
permission->allowed = FALSE;
permission->reset = permission->disallow_if_conditional_original_reset;
permission->disallow_if_conditional = FALSE;
tmp = permission->disallow_if_conditional_original_conditionals;
permission->disallow_if_conditional_original_conditionals =
permission->conditionals;
permission->conditionals = tmp;
g_ptr_array_set_size (
permission->disallow_if_conditional_original_conditionals, 0);
}
g_ptr_array_add (permission->conditionals, g_strdup (maybe_condition));
g_ptr_array_sort (permission->conditionals, flatpak_strcmp0_ptr);
}
}
static void
flatpak_permission_merge (FlatpakPermission *permission,
FlatpakPermission *other_permission)
{
if (other_permission->reset)
{
permission->reset = TRUE;
g_ptr_array_set_size (permission->conditionals, 0);
}
permission->allowed = other_permission->allowed;
for (size_t i = 0; i < other_permission->conditionals->len; i++)
{
const char *conditional = other_permission->conditionals->pdata[i];
/* Check if its already there */
if (g_ptr_array_find_with_equal_func (permission->conditionals,
conditional,
g_str_equal, NULL))
return;
g_ptr_array_add (permission->conditionals, g_strdup (conditional));
}
g_ptr_array_sort (permission->conditionals, flatpak_strcmp0_ptr);
/* Internal consistency check */
if (permission->allowed)
g_assert (permission->conditionals->len == 0);
}
static gboolean
flatpak_permission_compute_allowed (FlatpakPermission *permission,
FlatpakContextConditionEvaluator evaluator)
{
if (permission->allowed)
return TRUE;
for (size_t i = 0; i < permission->conditionals->len; i++)
{
const char *conditional = permission->conditionals->pdata[i];
gboolean negated;
const char *condition_str;
guint32 condition;
condition_str = parse_negated (conditional, &negated);
condition =
flatpak_context_bitmask_from_string (condition_str,
flatpak_context_conditions);
/* If condition is 0 it means this version of flatpak doesn't know
* about the condition and it cannot be satisfied. */
if (condition == 0)
continue;
/* Conditions which are always true in this version of flatpak */
if ((condition & flatpak_context_true_conditions) && !negated)
return TRUE;
/* Conditions which need runtime evaluation */
if (evaluator && evaluator (condition) == !negated)
return TRUE;
}
/* No condition evaluated to TRUE, so disable the thing */
return FALSE;
}
static gboolean
flatpak_permission_adds_permissions (FlatpakPermission *old,
FlatpakPermission *new)
{
size_t i = 0, j = 0;
if (old->allowed)
return FALSE;
if (new->allowed)
return TRUE;
if (new->conditionals->len > old->conditionals->len)
return TRUE;
while (TRUE)
{
const char *old_cond = old->conditionals->pdata[i];
const char *new_cond = new->conditionals->pdata[j];
int res;
if (old_cond == NULL)
return new_cond != NULL;
if (new_cond == NULL)
return FALSE;
res = strcmp (old_cond, new_cond);
if (res == 0) /* Same conditional */
{
i++;
j++;
}
else if (res < 0) /* Old conditional was removed */
{
i++;
}
else /* new conditional */
{
return FALSE;
}
}
return FALSE;
}
static GHashTable *
flatpak_permissions_new (void)
{
return g_hash_table_new_full (g_str_hash, g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) flatpak_permission_free);
}
static GHashTable *
flatpak_permissions_dup (GHashTable *old)
{
GHashTable *new;
const char *name;
FlatpakPermission *old_permission;
GHashTableIter iter;
new = flatpak_permissions_new ();
g_hash_table_iter_init (&iter, old);
while (g_hash_table_iter_next (&iter,
(gpointer *) &name,
(gpointer *) &old_permission))
{
g_hash_table_insert (new,
g_strdup (name),
flatpak_permission_dup (old_permission));
}
return new;
}
static FlatpakPermission *
flatpak_permissions_ensure (GHashTable *permissions,
const char *name)
{
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
if (permission == NULL)
{
permission = flatpak_permission_new ();
g_hash_table_insert (permissions, g_strdup (name), permission);
}
return permission;
}
static void
flatpak_permissions_set_not_allowed (GHashTable *permissions,
const char *name)
{
flatpak_permission_set_not_allowed (flatpak_permissions_ensure (permissions,
name));
}
static void
flatpak_permissions_set_allowed (GHashTable *permissions,
const char *name)
{
flatpak_permission_set_allowed (flatpak_permissions_ensure (permissions,
name));
}
static void
flatpak_permissions_set_allowed_if (GHashTable *permissions,
const char *name,
const char *condition)
{
flatpak_permission_set_allowed_if (flatpak_permissions_ensure (permissions,
name),
condition);
}
static gboolean
flatpak_permissions_allows_unconditionally (GHashTable *permissions,
const char *name)
{
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
if (permission)
return permission->allowed;
return FALSE;
}
static void
flatpak_permissions_to_args (GHashTable *permissions,
const char *argname,
GPtrArray *args)
{
g_autoptr(GList) ordered_keys = NULL;
ordered_keys = g_hash_table_get_keys (permissions);
ordered_keys = g_list_sort (ordered_keys, (GCompareFunc) strcmp);
for (GList *l = ordered_keys; l != NULL; l = l->next)
{
const char *name = l->data;
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
flatpak_permission_to_args (permission, argname, name, args);
}
}
static char **
flatpak_permissions_to_strv (GHashTable *permissions,
gboolean flatten)
{
g_autoptr(GList) ordered_keys = NULL;
g_autoptr(GPtrArray) res = g_ptr_array_new ();
ordered_keys = g_hash_table_get_keys (permissions);
ordered_keys = g_list_sort (ordered_keys, (GCompareFunc) strcmp);
for (GList *l = ordered_keys; l != NULL; l = l->next)
{
const char *name = l->data;
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
flatpak_permission_serialize (permission, name, res, flatten);
}
g_ptr_array_add (res, NULL);
return (char **)g_ptr_array_free (g_steal_pointer (&res), FALSE);
}
static guint32
flatpak_permissions_compute_allowed (GHashTable *permissions,
const char **names,
FlatpakContextConditionEvaluator evaluator)
{
guint32 bitmask = 0;
for (size_t i = 0; names[i] != NULL; i++)
{
const char *name = names[i];
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
if (permission &&
flatpak_permission_compute_allowed (permission, evaluator))
bitmask |= 1 << i;
}
return bitmask;
}
static gboolean
flatpak_permissions_from_strv (GHashTable *permissions,
const char **strv,
GError **error)
{
for (size_t i = 0; strv[i] != NULL; i++)
{
g_auto(GStrv) tokens = g_strsplit (strv[i], ":", 3);
const char *name = NULL;
gboolean negated = FALSE;
const char *condition = NULL;
FlatpakPermission *permission;
if (strcmp (tokens[0], "if") == 0)
{
if (g_strv_length (tokens) != 3)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
_("Invalid permission syntax: %s"), strv[i]);
return FALSE;
}
name = tokens[1];
condition = tokens[2];
}
else
{
if (g_strv_length (tokens) != 1)
{
g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_FAILED,
_("Invalid permission syntax: %s"), strv[i]);
return FALSE;
}
name = parse_negated (tokens[0], &negated);
}
permission = flatpak_permissions_ensure (permissions, name);
flatpak_permission_deserialize (permission, negated, condition);
}
return TRUE;
}
static void
flatpak_permissions_merge (GHashTable *permissions,
GHashTable *other)
{
const char *name;
FlatpakPermission *other_permission;
GHashTableIter iter;
g_hash_table_iter_init (&iter, other);
while (g_hash_table_iter_next (&iter,
(gpointer *) &name,
(gpointer *) &other_permission))
{
FlatpakPermission *permission = g_hash_table_lookup (permissions, name);
if (permission)
{
flatpak_permission_merge (permission, other_permission);
}
else
{
g_hash_table_insert (permissions,
g_strdup (name),
flatpak_permission_dup (other_permission));
}
}
}
static gboolean
flatpak_permissions_adds_permissions (GHashTable *old,
GHashTable *new)
{
const char *name;
FlatpakPermission *new_permission;
GHashTableIter iter;
g_hash_table_iter_init (&iter, new);
while (g_hash_table_iter_next (&iter,
(gpointer *) &name,
(gpointer *) &new_permission))
{
FlatpakPermission *old_permission = g_hash_table_lookup (old, name);
if (old_permission)
{
if (flatpak_permission_adds_permissions (old_permission,
new_permission))
return TRUE;
}
else
{
if (new_permission->allowed ||
new_permission->conditionals->len > 0)
return TRUE; /* new is completely new permission */
}
}
return FALSE;
}
#ifdef INCLUDE_INTERNAL_TESTS
static void flatpak_permissions_test_basic (void)
{
/* This is in canonical form, so must be kept sorted by name */
const char *perms_strv[] =
{
/* Regular unconditional allowed (resets) */
"allowed",
/* conditional allowed with two conditions (doesn't reset) */
"cond1", /* backwards compat */
"if:cond1:check1",
"if:cond1:check2",
/* conditional allowed with one conditions (doesn't reset) */
"cond2", /* backwards compat */
"if:cond2:check3",
/* conditional allowed (resets) */
"!cond3", /* reset */
"cond3", /* backwards compat */
"if:cond3:check3",
/* Regular unconditional disallowed (resets) */
"!disallowed",
NULL,
};
const char *perms_args[] =
{
"--socket=allowed",
"--socket-if=cond1:check1",
"--socket-if=cond1:check2",
"--socket-if=cond2:check3",
/* conditional allowed (resets) */
"--nosocket=cond3",
"--socket-if=cond3:check3",
/* Regular unconditional disallowed (resets) */
"--nosocket=disallowed",
NULL,
};
GError *error = NULL;
/* Test parsing */
g_autoptr(GHashTable) perms = flatpak_permissions_new ();
gboolean ok = flatpak_permissions_from_strv (perms, perms_strv, &error);
g_assert_true(ok);
g_assert_no_error(error);
g_assert_nonnull(perms);
g_assert_cmpint(g_hash_table_size (perms), ==, 5);
FlatpakPermission *allowed = g_hash_table_lookup (perms, "allowed");
g_assert_nonnull(allowed);
g_assert_true(allowed->allowed);
g_assert_true(allowed->reset);
g_assert(allowed->conditionals->len == 0);
FlatpakPermission *disallowed = g_hash_table_lookup (perms, "disallowed");
g_assert_nonnull(disallowed);
g_assert_false(disallowed->allowed);
g_assert_true(disallowed->reset);
g_assert(disallowed->conditionals->len == 0);
FlatpakPermission *cond1 = g_hash_table_lookup (perms, "cond1");
g_assert_nonnull(cond1);
g_assert_false(cond1->allowed);
g_assert_false(cond1->reset);
g_assert(cond1->conditionals->len == 2);
g_assert_cmpstr(cond1->conditionals->pdata[0], ==, "check1");
g_assert_cmpstr(cond1->conditionals->pdata[1], ==, "check2");
FlatpakPermission *cond2 = g_hash_table_lookup (perms, "cond2");
g_assert_nonnull(cond2);
g_assert_false(cond2->allowed);
g_assert_false(cond2->reset);
g_assert(cond2->conditionals->len == 1);
g_assert_cmpstr(cond2->conditionals->pdata[0], ==, "check3");
FlatpakPermission *cond3 = g_hash_table_lookup (perms, "cond3");
g_assert_nonnull(cond3);
g_assert_false(cond3->allowed);
g_assert_true(cond3->reset);
g_assert(cond3->conditionals->len == 1);
g_assert_cmpstr(cond3->conditionals->pdata[0], ==, "check3");
/* Test roundtrip */
g_auto(GStrv) new_strv = flatpak_permissions_to_strv (perms, FALSE);
g_assert_cmpstrv (perms_strv, new_strv);
g_autoptr(GPtrArray) args = g_ptr_array_new_with_free_func (g_free);
flatpak_permissions_to_args (perms, "socket", args);
g_ptr_array_add(args, NULL);
g_assert_cmpstrv (perms_args, args->pdata);
/* Test copy */
g_autoptr(FlatpakPermission) cond1_copy = flatpak_permission_dup(cond1);
g_assert_nonnull(cond1_copy);
g_assert_false(cond1_copy->allowed);
g_assert_false(cond1_copy->reset);
g_assert(cond1_copy->conditionals->len == 2);
g_assert_cmpstr(cond1_copy->conditionals->pdata[0], ==, "check1");
g_assert_cmpstr(cond1_copy->conditionals->pdata[1], ==, "check2");
/* Test setters: */
{
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
flatpak_permission_set_allowed (copy);
g_assert_true (copy->allowed);
g_assert_true (copy->reset);
g_assert(copy->conditionals->len == 0);
}
{
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
flatpak_permission_set_not_allowed (copy);
g_assert_false (copy->allowed);
g_assert_true (copy->reset);
g_assert(copy->conditionals->len == 0);
}
{
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
flatpak_permission_set_allowed_if (copy, "check0");
g_assert_false (copy->allowed);
g_assert_false (copy->reset);
g_assert(copy->conditionals->len == 3);
g_assert_cmpstr(copy->conditionals->pdata[0], ==, "check0");
g_assert_cmpstr(copy->conditionals->pdata[1], ==, "check1");
g_assert_cmpstr(copy->conditionals->pdata[2], ==, "check2");
}
/* Test merge */
{
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
flatpak_permission_merge (copy, allowed);
g_assert_true (copy->allowed);
g_assert_true (copy->reset);
g_assert(copy->conditionals->len == 0);
}
{
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
flatpak_permission_merge (copy, disallowed);
g_assert_false (copy->allowed);
g_assert_true (copy->reset);
g_assert(copy->conditionals->len == 0);
}
{
/* Merge from non-reset conditional */
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
flatpak_permission_merge (copy, cond2);
g_assert_false (copy->allowed);
g_assert_false (copy->reset);
g_assert(copy->conditionals->len == 3);
}
{
/* Merge from reset conditional */
g_autoptr(FlatpakPermission) copy = flatpak_permission_dup(cond1);
flatpak_permission_merge (copy, cond3);
g_assert_false (copy->allowed);
g_assert_true (copy->reset);
g_assert(copy->conditionals->len == 1);
}
}
static void flatpak_permissions_test_backwards_compat (void)
{
{
/* Deserialize if:wayland:foo;wayland
* The last wayland makes it unconditional. */
g_autoptr(FlatpakPermission) perm = flatpak_permission_new ();
flatpak_permission_deserialize (perm, FALSE, "foo");
flatpak_permission_deserialize (perm, FALSE, NULL);
g_assert_true (perm->allowed);
g_assert_true (perm->reset);
g_assert_cmpint (perm->conditionals->len, ==, 0);
}
{
/* Deserialize wayland;if:wayland:foo;wayland
* Should be the same as the one above. The first wayland is just for
* backwards compat. */
g_autoptr(FlatpakPermission) perm = flatpak_permission_new ();
flatpak_permission_deserialize (perm, FALSE, NULL);
flatpak_permission_deserialize (perm, FALSE, "foo");
flatpak_permission_deserialize (perm, FALSE, NULL);
g_assert_true (perm->allowed);
g_assert_true (perm->reset);
g_assert_cmpint (perm->conditionals->len, ==, 0);
}
{
/* Deserialize if:wayland:foo;wayland;if:wayland:bar
* Now the wayland is before a conditional, so it acts as backwards
* compat. */
g_autoptr(FlatpakPermission) perm = flatpak_permission_new ();
flatpak_permission_deserialize (perm, FALSE, "foo");
flatpak_permission_deserialize (perm, FALSE, NULL);
flatpak_permission_deserialize (perm, FALSE, "bar");
g_assert_false (perm->allowed);
g_assert_false (perm->reset);
g_assert_cmpint (perm->conditionals->len, ==, 2);
g_assert_cmpstr (perm->conditionals->pdata[0], ==, "bar");
g_assert_cmpstr (perm->conditionals->pdata[1], ==, "foo");
}
}
FLATPAK_INTERNAL_TEST("/context/permissions/basic",
flatpak_permissions_test_basic);
FLATPAK_INTERNAL_TEST("/context/permissions/backwards-compat",
flatpak_permissions_test_backwards_compat);
#endif /* INCLUDE_INTERNAL_TESTS */
FlatpakContext *
flatpak_context_new (void)
{