From b262bbbd2549eab120b7ca002ca37900c19a5d2c Mon Sep 17 00:00:00 2001 From: Julian Bouzas Date: Fri, 30 Jan 2026 13:44:50 -0500 Subject: [PATCH] lib: Add new WpPermissionManager API This allows setting object specific permissions on any client easily. --- lib/wp/meson.build | 2 + lib/wp/permission-manager.c | 670 ++++++++++++++++++++++++++++ lib/wp/permission-manager.h | 84 ++++ lib/wp/private/permission-manager.h | 26 ++ lib/wp/wp.h | 1 + 5 files changed, 783 insertions(+) create mode 100644 lib/wp/permission-manager.c create mode 100644 lib/wp/permission-manager.h create mode 100644 lib/wp/private/permission-manager.h diff --git a/lib/wp/meson.build b/lib/wp/meson.build index 9aacc624..c3ff7473 100644 --- a/lib/wp/meson.build +++ b/lib/wp/meson.build @@ -22,6 +22,7 @@ wp_lib_sources = files( 'object.c', 'object-interest.c', 'object-manager.c', + 'permission-manager.c', 'plugin.c', 'port.c', 'proc-utils.c', @@ -69,6 +70,7 @@ wp_lib_headers = files( 'object.h', 'object-interest.h', 'object-manager.h', + 'permission-manager.h', 'plugin.h', 'port.h', 'proc-utils.h', diff --git a/lib/wp/permission-manager.c b/lib/wp/permission-manager.c new file mode 100644 index 00000000..1f059e5b --- /dev/null +++ b/lib/wp/permission-manager.c @@ -0,0 +1,670 @@ +/* WirePlumber + * + * Copyright © 2026 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +#include "private/permission-manager.h" +#include "permission-manager.h" +#include "proxy-interfaces.h" +#include "object-manager.h" +#include "json-utils.h" +#include "error.h" +#include "core.h" +#include "log.h" + +WP_DEFINE_LOCAL_LOG_TOPIC ("wp-permission-manager") + +/*! \defgroup wppermissionmanager WpPermissionManager */ +/*! + * \struct WpPermissionManager + * + * The WpPermissionManager class is in charge of updating automatically + * permissions on interested objects every time they are added or removed for + * a particular client. + * + * WpPermissionManager API. + */ + +typedef struct _PermissionMatch PermissionMatch; +struct _PermissionMatch +{ + guint32 id; + guint32 permissions; + GClosure *closure; + WpObjectInterest *interest; + WpSpaJson *rules; +}; + +static guint +get_next_id () +{ + static guint32 next_id = 0; + g_atomic_int_inc (&next_id); + return next_id; +} + +static PermissionMatch * +permission_match_new (guint32 perms, GClosure *closure, + WpObjectInterest * interest, WpSpaJson * rules) +{ + PermissionMatch *match = g_new0 (PermissionMatch, 1); + match->id = get_next_id (); + match->permissions = perms; + match->closure = closure ? g_closure_ref (closure) : NULL; + match->interest = interest ? wp_object_interest_ref (interest) : NULL; + match->rules = rules ? wp_spa_json_ref (rules) : NULL; + return match; +} + +static void +permission_interest_free (PermissionMatch *self) +{ + g_clear_pointer (&self->closure, g_closure_unref); + g_clear_pointer (&self->interest, wp_object_interest_unref); + g_clear_pointer (&self->rules, wp_spa_json_unref); + g_free (self); +} + +struct _WpPermissionManager +{ + WpObject parent; + + guint32 default_perms; + GPtrArray *clients; + GHashTable *matches; + + WpObjectManager *om; +}; + +G_DEFINE_TYPE (WpPermissionManager, wp_permission_manager, WP_TYPE_OBJECT) + +static void +wp_permission_manager_init (WpPermissionManager * self) +{ + /* Init default permissions to all */ + self->default_perms = PW_PERM_R | PW_PERM_W | PW_PERM_X; + + /* Init permission interests table */ + self->matches = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, + (GDestroyNotify)permission_interest_free); + + /* Init clients list */ + self->clients = g_ptr_array_new_with_free_func ( + (GDestroyNotify) g_object_unref); +} + +enum { + STEP_LOAD = WP_TRANSITION_STEP_CUSTOM_START, +}; + +static WpObjectFeatures +wp_permission_manager_get_supported_features (WpObject * self) +{ + return WP_PERMISSION_MANAGER_LOADED; +} + +static guint +wp_permission_manager_activate_get_next_step (WpObject * self, + WpFeatureActivationTransition * transition, guint step, + WpObjectFeatures missing) +{ + g_return_val_if_fail (missing == WP_PERMISSION_MANAGER_LOADED, + WP_TRANSITION_STEP_ERROR); + + return STEP_LOAD; +} + +static guint32 +invoke_permissions_closure (WpPermissionManager *self, WpClient *client, + WpGlobalProxy *object, GClosure *closure) +{ + GValue args[3] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; + GValue ret = G_VALUE_INIT; + guint32 perms; + + g_value_init (&args[0], WP_TYPE_PERMISSION_MANAGER); + g_value_set_object (&args[0], self); + g_value_init (&args[1], WP_TYPE_CLIENT); + g_value_set_object (&args[1], client); + g_value_init (&args[2], WP_TYPE_GLOBAL_PROXY); + g_value_set_object (&args[2], object); + g_value_init (&ret, G_TYPE_UINT); + + g_closure_invoke (closure, &ret, 3, args, NULL); + perms = g_value_get_uint (&ret); + + g_value_unset (&args[0]); + g_value_unset (&args[1]); + g_value_unset (&args[2]); + g_value_unset (&ret); + + return perms; +} + +typedef struct _MatchRulesCallbackData MatchRulesCallbackData; +struct _MatchRulesCallbackData { + gboolean matched; + guint32 perms; +}; + +static gboolean +match_rules_cb (gpointer data, const gchar * action, WpSpaJson * value, + GError ** e) +{ + MatchRulesCallbackData *cb_data = (MatchRulesCallbackData *)data; + g_autofree gchar *perms_str = NULL; + guint32 perms = 0; + + if (!g_str_equal (action, "set-permissions")) { + if (e) + g_set_error (e, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT, + "Action name '%s' is not valid", action); + return FALSE; + } + + if (!wp_spa_json_is_string (value)) { + if (e) + g_set_error (e, WP_DOMAIN_LIBRARY, WP_LIBRARY_ERROR_INVALID_ARGUMENT, + "Action '%s' must be a string", action); + return FALSE; + } + + /* Parse permissions */ + perms_str = wp_spa_json_parse_string (value); + if (g_strcmp0 (perms_str, "all") == 0) { + perms = PW_PERM_ALL; + } else { + for (guint i = 0; i < strlen (perms_str); i++) { + switch (perms_str[i]) { + case 'r': perms |= PW_PERM_R; break; + case 'w': perms |= PW_PERM_W; break; + case 'x': perms |= PW_PERM_X; break; + case 'm': perms |= PW_PERM_M; break; + case 'l': perms |= PW_PERM_L; break; + case '-': break; + default: { + if (e) + g_set_error (e, WP_DOMAIN_LIBRARY, + WP_LIBRARY_ERROR_INVALID_ARGUMENT, + "Permissions '%s' are not valid", perms_str); + return FALSE; + } + } + } + } + + if (cb_data) { + cb_data->matched = TRUE; + cb_data->perms |= perms; + } + return TRUE; +} + +static gboolean +get_rules_matched_object_permissions (WpPermissionManager *self, + WpSpaJson *rules, WpGlobalProxy *object, guint32 *perms) +{ + g_autoptr (GError) e = NULL; + g_autoptr (WpProperties) gp_props = NULL; + g_autoptr (WpProperties) po_props = NULL; + MatchRulesCallbackData data = { FALSE, 0 }; + + /* Check global proxy properties */ + gp_props = wp_global_proxy_get_global_properties (object); + if (gp_props && !wp_json_utils_match_rules (rules, gp_props, match_rules_cb, + &data, &e)) + goto error; + + /* Also check pipewire object properties if it is a pipewire object */ + if (WP_IS_PIPEWIRE_OBJECT (object)) { + po_props = wp_pipewire_object_get_properties (WP_PIPEWIRE_OBJECT (object)); + if (po_props && !wp_json_utils_match_rules (rules, po_props, match_rules_cb, + &data, &e)) + goto error; + } + + /* Set permissions if there was a match */ + if (data.matched && perms) + *perms = data.perms; + + return data.matched; + +error: + wp_warning_object (self, "Malformed JSON match rules: %s", e->message); + return FALSE; +} + +static gboolean +get_matched_object_permissions (WpPermissionManager *self, PermissionMatch *m, + WpClient *client, WpGlobalProxy *object, guint32 *perms) +{ + /* Check interest */ + if (m->interest && wp_object_interest_matches (m->interest, object)) { + if (!perms) + return TRUE; + *perms = m->closure ? invoke_permissions_closure (self, client, object, + m->closure) : m->permissions; + return TRUE; + } + + /* Check rules */ + if (m->rules) + return get_rules_matched_object_permissions (self, m->rules, object, perms); + + return FALSE; +} + +static GArray * +build_permissions_array (WpPermissionManager *self, WpClient *client) +{ + g_autoptr (WpIterator) it = NULL; + g_auto (GValue) value = G_VALUE_INIT; + struct pw_permission def_perm = { PW_ID_ANY, self->default_perms }; + GArray *arr = g_array_new (FALSE, FALSE, sizeof (struct pw_permission)); + + /* Add default permissions */ + g_array_append_val (arr, def_perm); + + /* Add object specific permissions in the array */ + it = wp_object_manager_new_iterator (self->om); + for (; wp_iterator_next (it, &value); g_value_unset (&value)) { + WpGlobalProxy *object = g_value_get_object (&value); + GHashTableIter iter; + PermissionMatch *match = NULL; + g_hash_table_iter_init (&iter, self->matches); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&match)) { + guint32 perms = PW_PERM_INVALID; + if (get_matched_object_permissions (self, match, client, object, &perms) + && perms != PW_PERM_INVALID) { + struct pw_permission obj_perm = { 0, }; + obj_perm.id = wp_proxy_get_bound_id (WP_PROXY (object)); + obj_perm.permissions = perms; + g_array_append_val (arr, obj_perm);; + } + } + } + + /* Merge permissions with same object ID */ + for (guint i = 0; i < arr->len; i++) { + for (guint j = i + 1; j < arr->len; ) { + struct pw_permission *a = &g_array_index (arr, struct pw_permission, i); + struct pw_permission *b = &g_array_index (arr, struct pw_permission, j); + if (a->id == b->id) { + a->permissions |= b->permissions; + g_array_remove_index (arr, j); + } else { + j++; + } + } + } + + return arr; +} + +static void +update_client_permissions (WpPermissionManager *self, WpClient *client) +{ + guint32 bound_id = 0; + g_autoptr (GArray) perms = NULL; + + /* Dont do anything if the permission manager is not activated */ + if (!(wp_object_get_active_features (WP_OBJECT (self)) & + WP_PERMISSION_MANAGER_LOADED)) + return; + + /* Make sure the client proxy is still valid */ + if (!wp_proxy_get_pw_proxy (WP_PROXY (client))) + return; + + bound_id = wp_proxy_get_bound_id (WP_PROXY (client)); + perms = build_permissions_array (self, client); + + wp_info_object (self, + "Updating permissions on client %u: any=%c%c%c%c%c len=%u", + bound_id, + !!(self->default_perms & PW_PERM_R) ? 'r' : '-', + !!(self->default_perms & PW_PERM_W) ? 'w' : '-', + !!(self->default_perms & PW_PERM_X) ? 'x' : '-', + !!(self->default_perms & PW_PERM_M) ? 'm' : '-', + !!(self->default_perms & PW_PERM_L) ? 'l' : '-', + perms->len); + + wp_client_update_permissions_array (client, perms->len, + (const struct pw_permission *) perms->data); +} + +static gboolean +has_object_match (WpPermissionManager *self, WpGlobalProxy *object) +{ + GHashTableIter iter; + PermissionMatch *m = NULL; + + g_hash_table_iter_init (&iter, self->matches); + while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&m)) { + if (m->interest && wp_object_interest_matches (m->interest, object)) + return TRUE; + if (m->rules && get_rules_matched_object_permissions (self, m->rules, + object, NULL)) + return TRUE; + } + + return FALSE; +} + +static void +update_permissions (WpPermissionManager *self) +{ + for (guint i = 0; i < self->clients->len; i++) { + WpClient *client = g_ptr_array_index (self->clients, i); + update_client_permissions (self, client); + } +} + +static void +on_object_added_or_removed (WpObjectManager *om, WpGlobalProxy *object, + gpointer d) +{ + WpPermissionManager * self = WP_PERMISSION_MANAGER (d); + + if (has_object_match (self, object)) + update_permissions (self); +} + +static void +on_object_manager_installed (WpObjectManager *om, gpointer d) +{ + WpTransition * transition = WP_TRANSITION (d); + WpPermissionManager * self = wp_transition_get_source_object (transition); + + wp_object_update_features (WP_OBJECT (self), WP_PERMISSION_MANAGER_LOADED, 0); +} + +static void +wp_permission_manager_activate_execute_step (WpObject * object, + WpFeatureActivationTransition * transition, guint step, + WpObjectFeatures missing) +{ + WpPermissionManager *self = WP_PERMISSION_MANAGER (object); + g_autoptr (WpCore) core = wp_object_get_core (object); + + switch (step) { + case STEP_LOAD: { + /* Install object manager */ + g_clear_object (&self->om); + self->om = wp_object_manager_new (); + wp_object_manager_add_interest (self->om, WP_TYPE_GLOBAL_PROXY, NULL); + wp_object_manager_request_object_features (self->om, + WP_TYPE_GLOBAL_PROXY, WP_PIPEWIRE_OBJECT_FEATURES_MINIMAL); + g_signal_connect_object (self->om, "object-added", + G_CALLBACK (on_object_added_or_removed), self, 0); + g_signal_connect_object (self->om, "object-removed", + G_CALLBACK (on_object_added_or_removed), self, 0); + g_signal_connect_object (self->om, "installed", + G_CALLBACK (on_object_manager_installed), transition, 0); + wp_core_install_object_manager (core, self->om); + break; + } + + case WP_TRANSITION_STEP_ERROR: + break; + default: + g_assert_not_reached (); + } +} + +static void +wp_permission_manager_deactivate (WpObject * object, WpObjectFeatures features) +{ + WpPermissionManager *self = WP_PERMISSION_MANAGER (object); + + g_clear_object (&self->om); + + wp_object_update_features (WP_OBJECT (self), 0, WP_OBJECT_FEATURES_ALL); +} + +static void +wp_permission_manager_finalize (GObject * object) +{ + WpPermissionManager *self = WP_PERMISSION_MANAGER (object); + + g_clear_pointer (&self->clients, g_ptr_array_unref); + g_clear_pointer (&self->matches, g_hash_table_unref); + + g_clear_object (&self->om); + + G_OBJECT_CLASS (wp_permission_manager_parent_class)->finalize (object); +} + +static void +wp_permission_manager_class_init (WpPermissionManagerClass * klass) +{ + GObjectClass * object_class = (GObjectClass *) klass; + WpObjectClass *wpobject_class = (WpObjectClass *) klass; + + object_class->finalize = wp_permission_manager_finalize; + + wpobject_class->get_supported_features = + wp_permission_manager_get_supported_features; + wpobject_class->activate_get_next_step = + wp_permission_manager_activate_get_next_step; + wpobject_class->activate_execute_step = + wp_permission_manager_activate_execute_step; + wpobject_class->deactivate = wp_permission_manager_deactivate; +} + +void +wp_permission_manager_add_client (WpPermissionManager *self, WpClient *client) +{ + g_return_if_fail (WP_IS_PERMISSION_MANAGER (self)); + + g_ptr_array_add (self->clients, g_object_ref (client)); + update_client_permissions (self, client); +} + +void +wp_permission_manager_remove_client (WpPermissionManager *self, + WpClient *client) +{ + g_return_if_fail (WP_IS_PERMISSION_MANAGER (self)); + + g_ptr_array_remove_fast (self->clients, client); + update_client_permissions (self, client); +} + +/*! + * \brief Creates a new WpPermissionManager object + * + * \ingroup wppermissionmanager + * \param core the WpCore + * \returns (transfer full): a new WpPermissionManager object + */ +WpPermissionManager * +wp_permission_manager_new (WpCore * core) +{ + g_return_val_if_fail (core, NULL); + + return g_object_new (WP_TYPE_PERMISSION_MANAGER, "core", core, NULL); +} + +/*! + * \brief Sets the default permissions that will be applied to all objects that + * don't match any interest + * + * \ingroup wppermissionmanager + * \param self the permission manager + * \param permissions the default permissions to apply + */ +void +wp_permission_manager_set_default_permissions (WpPermissionManager *self, + guint32 permissions) +{ + g_return_if_fail (WP_IS_PERMISSION_MANAGER (self)); + + if (self->default_perms != permissions) { + self->default_perms = permissions; + update_permissions (self); + } +} + +static guint32 +wp_permission_manager_add_match (WpPermissionManager *self, + PermissionMatch *match) +{ + guint id = match->id; + g_hash_table_insert (self->matches, GUINT_TO_POINTER (id), match); + update_permissions (self); + return id; +} + +/*! + * \brief Adds an interest match to apply permissions with callback in matched + * objects. + * + * Interest consists of a GType that the object must be an ancestor of + * (g_type_is_a() must match) and optionally, a set of additional constraints + * on certain properties of the object. Refer to WpObjectInterest for more details. + * + * \ingroup wppermissionmanager + * \param self the permission manager + * \param callback (scope async): the permissions match callback + * \param user_data data to pass to \a callback + * \param interest (transfer full): the interest + * \returns the added match ID, or SPA_ID_INVALID if error + */ +guint32 +wp_permission_manager_add_interest_match (WpPermissionManager *self, + WpPermissionMatchCallback callback, gpointer user_data, + WpObjectInterest * interest) +{ + GClosure *closure = g_cclosure_new (G_CALLBACK (callback), user_data, NULL); + return wp_permission_manager_add_interest_match_closure (self, closure, + interest); +} + +/*! + * \brief Adds an interest match to apply permissions with closure in matched + * objects. + * + * Interest consists of a GType that the object must be an ancestor of + * (g_type_is_a() must match) and optionally, a set of additional constraints + * on certain properties of the object. Refer to WpObjectInterest for more details. + * + * \ingroup wppermissionmanager + * \param self the permission manager + * \param closure (transfer full): the closure to apply permissions + * \param interest (transfer full): the interest + * \returns the added match ID, or SPA_ID_INVALID if error + */ +guint32 +wp_permission_manager_add_interest_match_closure (WpPermissionManager *self, + GClosure *closure, WpObjectInterest * interest) +{ + g_autoptr (WpObjectInterest) i = interest; + g_autoptr (GClosure) c = closure; + PermissionMatch *match; + + g_return_val_if_fail (WP_IS_PERMISSION_MANAGER (self), SPA_ID_INVALID); + g_return_val_if_fail (closure, SPA_ID_INVALID); + g_return_val_if_fail (i, SPA_ID_INVALID); + + if (G_CLOSURE_NEEDS_MARSHAL (closure)) + g_closure_set_marshal (closure, g_cclosure_marshal_generic); + + match = permission_match_new (PW_PERM_INVALID, c, i, NULL); + return wp_permission_manager_add_match (self, match); +} + +/*! + * \brief Adds an interest match to apply same permissions in matched objects. + * + * Interest consists of a GType that the object must be an ancestor of + * (g_type_is_a() must match) and optionally, a set of additional constraints + * on certain properties of the object. Refer to WpObjectInterest for more details. + * + * \ingroup wppermissionmanager + * \param self the permission manager + * \param permissions the permissions to apply + * \param interest (transfer full): the interest + * \returns the added match ID, or SPA_ID_INVALID if error + */ +guint32 +wp_permission_manager_add_interest_match_simple (WpPermissionManager *self, + guint32 permissions, WpObjectInterest * interest) +{ + g_autoptr (WpObjectInterest) i = interest; + PermissionMatch *match; + + g_return_val_if_fail (WP_IS_PERMISSION_MANAGER (self), SPA_ID_INVALID); + g_return_val_if_fail (i, SPA_ID_INVALID); + + match = permission_match_new (permissions, NULL, i, NULL); + return wp_permission_manager_add_match (self, match); +} + +/*! + * \brief Adds a rules match to apply permissions in matched objects. + * + * The rules must be defined in a JSON object using the same format as all + * the wireplumber/pipewire rules. + * + * \ingroup wppermissionmanager + * \param self the permission manager + * \param rules (transfer full): the JSON rules + * \returns the added match ID, or SPA_ID_INVALID if error + */ +guint32 +wp_permission_manager_add_rules_match (WpPermissionManager *self, + WpSpaJson *rules) +{ + g_autoptr (WpSpaJson) r = rules; + PermissionMatch *match; + + g_return_val_if_fail (WP_IS_PERMISSION_MANAGER (self), SPA_ID_INVALID); + g_return_val_if_fail (r, SPA_ID_INVALID); + + match = permission_match_new (PW_PERM_INVALID, NULL, NULL, rules); + return wp_permission_manager_add_match (self, match); +} + +/*! + * \brief Removes the previously added match so that the associated permissions + * are not applied anymore. + * + * \ingroup wppermissionmanager + * \param self the permission manager + * \param match_id the match ID to remove + */ +void +wp_permission_manager_remove_match (WpPermissionManager *self, guint32 match_id) +{ + g_return_if_fail (WP_IS_PERMISSION_MANAGER (self)); + g_return_if_fail (match_id != SPA_ID_INVALID); + + g_hash_table_remove (self->matches, GUINT_TO_POINTER (match_id)); + update_permissions (self); +} + +/*! + * \brief Updates permissions on all clients the permission manager has. + * + * The permission manager already updates permissions on all clients + * automatically when a new client or object is added, however, this might be + * needed if interests with closures or callbacks were added and something + * changed externally. + * + * \ingroup wppermissionmanager + * \param self the permission manager + */ +void +wp_permission_manager_update_permissions (WpPermissionManager *self) +{ + g_return_if_fail (WP_IS_PERMISSION_MANAGER (self)); + + update_permissions (self); +} diff --git a/lib/wp/permission-manager.h b/lib/wp/permission-manager.h new file mode 100644 index 00000000..0ef03444 --- /dev/null +++ b/lib/wp/permission-manager.h @@ -0,0 +1,84 @@ +/* WirePlumber + * + * Copyright © 2026 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#ifndef __WIREPLUMBER_PERMISSION_MANAGER_H__ +#define __WIREPLUMBER_PERMISSION_MANAGER_H__ + +#include "object-interest.h" +#include "global-proxy.h" + +G_BEGIN_DECLS + +/*! + * \brief Flags to be used as WpObjectFeatures for WpPermissionManager. + * \ingroup wppermissionmanager + */ +typedef enum { /*< flags >*/ + /*! Loads the permission manager */ + WP_PERMISSION_MANAGER_LOADED = (1 << 0), +} WpPermissionManagerFeatures; + +/*! + * \brief The WpPermissionManager GType + * \ingroup wppermissionmanager + */ +#define WP_TYPE_PERMISSION_MANAGER (wp_permission_manager_get_type ()) + +WP_API +G_DECLARE_FINAL_TYPE (WpPermissionManager, wp_permission_manager, WP, + PERMISSION_MANAGER, WpObject) + +typedef struct _WpClient WpClient; + +/*! + * \brief callback to set permissions on the matched global object + * + * \ingroup wpsettings + * \param self the permission manager + * \param client the client that will have its permissions updated + * \param object the matched global + * \param user_data the passed data + */ +typedef guint32 (*WpPermissionMatchCallback) (WpPermissionManager *self, + WpClient *client, WpGlobalProxy *object, gpointer user_data); + +WP_API +WpPermissionManager * wp_permission_manager_new (WpCore * core); + +WP_API +void wp_permission_manager_set_default_permissions ( + WpPermissionManager *self, guint32 permissions); + +WP_API +guint32 wp_permission_manager_add_interest_match (WpPermissionManager *self, + WpPermissionMatchCallback callback, gpointer user_data, + WpObjectInterest * interest); + +WP_API +guint32 wp_permission_manager_add_interest_match_closure ( + WpPermissionManager *self, GClosure *closure, WpObjectInterest * interest); + +WP_API +guint32 wp_permission_manager_add_interest_match_simple ( + WpPermissionManager *self, guint32 permissions, + WpObjectInterest * interest); + +WP_API +guint32 wp_permission_manager_add_rules_match (WpPermissionManager *self, + WpSpaJson *rules); + +WP_API +void wp_permission_manager_remove_match (WpPermissionManager *self, + guint32 match_id); + +WP_API +void wp_permission_manager_update_permissions (WpPermissionManager *self); + +G_END_DECLS + +#endif diff --git a/lib/wp/private/permission-manager.h b/lib/wp/private/permission-manager.h new file mode 100644 index 00000000..766e731c --- /dev/null +++ b/lib/wp/private/permission-manager.h @@ -0,0 +1,26 @@ +/* WirePlumber + * + * Copyright © 2026 Collabora Ltd. + * @author Julian Bouzas + * + * SPDX-License-Identifier: MIT + */ + +#ifndef __WIREPLUMBER_PRIVATE_PERMISSION_MANAGER_H__ +#define __WIREPLUMBER_PRIVATE_PERMISSION_MANAGER_H__ + +#include "client.h" + +G_BEGIN_DECLS + +typedef struct _WpPermissionManager WpPermissionManager; + +void wp_permission_manager_add_client (WpPermissionManager *self, + WpClient *client); + +void wp_permission_manager_remove_client (WpPermissionManager *self, + WpClient *client); + +G_END_DECLS + +#endif diff --git a/lib/wp/wp.h b/lib/wp/wp.h index 3e010f96..7ef4e685 100644 --- a/lib/wp/wp.h +++ b/lib/wp/wp.h @@ -47,6 +47,7 @@ #include "wpversion.h" #include "factory.h" #include "settings.h" +#include "permission-manager.h" G_BEGIN_DECLS