From 1f0c590f498b0a2c2fb4d0347d3b799000baf921 Mon Sep 17 00:00:00 2001 From: Torkel Niklasson Date: Wed, 29 Apr 2026 08:10:22 +0200 Subject: [PATCH 1/2] docs: add WpPermissionManager API page and document permission managers in access config --- docs/rst/daemon/configuration/access.rst | 59 +++++++++++++++++++ docs/rst/library/c_api.rst | 1 + docs/rst/library/c_api/meson.build | 1 + .../library/c_api/permission_manager_api.rst | 17 ++++++ 4 files changed, 78 insertions(+) create mode 100644 docs/rst/library/c_api/permission_manager_api.rst diff --git a/docs/rst/daemon/configuration/access.rst b/docs/rst/daemon/configuration/access.rst index 5a366ca4..6d5ff945 100644 --- a/docs/rst/daemon/configuration/access.rst +++ b/docs/rst/daemon/configuration/access.rst @@ -58,3 +58,62 @@ Possible permissions are any combination of: client can't "see" (i.e. the client doesn't have ``r`` permission on them) The special value ``all`` is also supported and it is synonym for ``rwxm`` + +Permission Managers +------------------- + +For more advanced use cases, WirePlumber supports *permission managers* that can +apply per-object permissions dynamically based on rules and object interests. +Permission managers are defined in the ``access.permission-managers`` section +and then referenced by name in ``access.rules``. + +Example: + +.. code-block:: + + access.permission-managers = [ + { + name = "custom" + default_permissions = "all" + rules = [ + { + matches = [ + { + media.class = "Audio/Source" + } + ] + actions = { + set-permissions = "-" + } + } + ] + } + ] + + access.rules = [ + { + matches = [ + { + application.name = "paplay" + } + ] + actions = { + update-props = { + permission_manager_name = "custom" + } + } + } + ] + +Each permission manager supports the following properties: + + * ``name``: (required) a unique name used to reference the manager from + ``access.rules`` + * ``default_permissions``: the fallback permissions applied to all objects + that don't match any rule (applied as ``PW_ID_ANY``) + * ``rules``: a list of match rules with ``set-permissions`` actions that + grant specific permissions to objects matching the given constraints + +When both ``default_permissions`` and ``permission_manager_name`` are set in +a rule's ``update-props`` action, ``default_permissions`` takes precedence and +the permission manager is ignored. diff --git a/docs/rst/library/c_api.rst b/docs/rst/library/c_api.rst index 21eddb4a..c79ec852 100644 --- a/docs/rst/library/c_api.rst +++ b/docs/rst/library/c_api.rst @@ -23,6 +23,7 @@ C API Documentation c_api/link_api.rst c_api/device_api.rst c_api/client_api.rst + c_api/permission_manager_api.rst c_api/metadata_api.rst c_api/spa_device_api.rst c_api/impl_node_api.rst diff --git a/docs/rst/library/c_api/meson.build b/docs/rst/library/c_api/meson.build index 6857c1f8..f3f0c202 100644 --- a/docs/rst/library/c_api/meson.build +++ b/docs/rst/library/c_api/meson.build @@ -18,6 +18,7 @@ sphinx_files += files( 'obj_manager_api.rst', 'object_api.rst', 'pipewire_object_api.rst', + 'permission_manager_api.rst', 'plugin_api.rst', 'port_api.rst', 'properties_api.rst', diff --git a/docs/rst/library/c_api/permission_manager_api.rst b/docs/rst/library/c_api/permission_manager_api.rst new file mode 100644 index 00000000..9c31e5f1 --- /dev/null +++ b/docs/rst/library/c_api/permission_manager_api.rst @@ -0,0 +1,17 @@ +.. _permission_manager_api: + +WpPermissionManager +=================== +.. graphviz:: + :align: center + + digraph inheritance { + rankdir=LR; + GObject -> WpObject; + WpObject -> WpPermissionManager; + } + +.. doxygenstruct:: WpPermissionManager + +.. doxygengroup:: wppermissionmanager + :content-only: From 26f5fc11a671420003e9f3fb42aad52085bd7b60 Mon Sep 17 00:00:00 2001 From: Torkel Niklasson Date: Tue, 28 Apr 2026 15:14:26 +0200 Subject: [PATCH 2/2] permission-manager: Add core_permissions support The core object (ID 0) is implicit in the PipeWire connection and never appears in the permission manager's ObjectManager. Add a core_permissions field to set explicit permissions on it independently of default_permissions. --- docs/rst/daemon/configuration/access.rst | 6 +++ lib/wp/permission-manager.c | 37 +++++++++++++++++++ lib/wp/permission-manager.h | 4 ++ modules/module-lua-scripting/api/api.c | 25 ++++++++++++- .../wireplumber.conf.d.examples/access.conf | 6 +++ src/scripts/client/find-config-access.lua | 5 +++ 6 files changed, 81 insertions(+), 2 deletions(-) diff --git a/docs/rst/daemon/configuration/access.rst b/docs/rst/daemon/configuration/access.rst index 6d5ff945..2696a66c 100644 --- a/docs/rst/daemon/configuration/access.rst +++ b/docs/rst/daemon/configuration/access.rst @@ -75,6 +75,7 @@ Example: { name = "custom" default_permissions = "all" + core_permissions = "rx" rules = [ { matches = [ @@ -111,6 +112,11 @@ Each permission manager supports the following properties: ``access.rules`` * ``default_permissions``: the fallback permissions applied to all objects that don't match any rule (applied as ``PW_ID_ANY``) + * ``core_permissions``: permissions applied specifically to the PipeWire core + object (``PW_ID_CORE``, ID 0). This is useful when you want to allow a + client to interact with the core (e.g. enumerate objects, subscribe to + events) while restricting access to individual objects. If not set, the + ``default_permissions`` value is used for the core as well. * ``rules``: a list of match rules with ``set-permissions`` actions that grant specific permissions to objects matching the given constraints diff --git a/lib/wp/permission-manager.c b/lib/wp/permission-manager.c index 48f193ed..0e5a384b 100644 --- a/lib/wp/permission-manager.c +++ b/lib/wp/permission-manager.c @@ -76,6 +76,7 @@ struct _WpPermissionManager WpObject parent; guint32 default_perms; + guint32 core_perms; GPtrArray *clients; GHashTable *matches; @@ -90,6 +91,9 @@ wp_permission_manager_init (WpPermissionManager * self) /* Init default permissions to all */ self->default_perms = PW_PERM_R | PW_PERM_W | PW_PERM_X; + /* Core permissions not set by default (inherit from default_perms) */ + self->core_perms = PW_PERM_INVALID; + /* Init permission interests table */ self->matches = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)permission_interest_free); @@ -271,6 +275,13 @@ build_permissions_array (WpPermissionManager *self, WpClient *client) /* Add default permissions */ g_array_append_val (arr, def_perm); + /* Add core permissions if explicitly set (core is not in the OM since it is + * implicit in the PipeWire connection and not sent through the registry) */ + if (self->core_perms != PW_PERM_INVALID) { + struct pw_permission core_perm = { PW_ID_CORE, self->core_perms }; + g_array_append_val (arr, core_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)) { @@ -512,6 +523,32 @@ wp_permission_manager_set_default_permissions (WpPermissionManager *self, } } +/*! + * \brief Sets the permissions that will be applied to the core object (ID 0). + * + * The core object is not visible to the permission manager's object manager + * because it is implicit in the PipeWire connection and not sent through the + * registry. This method allows setting explicit permissions on it, independent + * of the default permissions. + * + * If not set (or set to PW_PERM_INVALID), the core inherits default_permissions. + * + * \ingroup wppermissionmanager + * \param self the permission manager + * \param permissions the permissions to apply to the core object + */ +void +wp_permission_manager_set_core_permissions (WpPermissionManager *self, + guint32 permissions) +{ + g_return_if_fail (WP_IS_PERMISSION_MANAGER (self)); + + if (self->core_perms != permissions) { + self->core_perms = permissions; + update_permissions (self); + } +} + static guint32 wp_permission_manager_add_match (WpPermissionManager *self, PermissionMatch *match) diff --git a/lib/wp/permission-manager.h b/lib/wp/permission-manager.h index d0708982..073dfb66 100644 --- a/lib/wp/permission-manager.h +++ b/lib/wp/permission-manager.h @@ -54,6 +54,10 @@ WP_API void wp_permission_manager_set_default_permissions ( WpPermissionManager *self, guint32 permissions); +WP_API +void wp_permission_manager_set_core_permissions ( + WpPermissionManager *self, guint32 permissions); + WP_API guint32 wp_permission_manager_add_interest_match (WpPermissionManager *self, WpPermissionMatchCallback callback, gpointer user_data, diff --git a/modules/module-lua-scripting/api/api.c b/modules/module-lua-scripting/api/api.c index 8374807b..3b9dfdde 100644 --- a/modules/module-lua-scripting/api/api.c +++ b/modules/module-lua-scripting/api/api.c @@ -2657,8 +2657,28 @@ permission_manager_set_default_permissions (lua_State *L) } wp_permission_manager_set_default_permissions (pm, perms); - lua_pushboolean (L, TRUE); - return 1; + return 0; +} + +static int +permission_manager_set_core_permissions (lua_State *L) +{ + WpPermissionManager *pm = wplua_checkobject (L, 1, + WP_TYPE_PERMISSION_MANAGER); + guint32 perms = PW_PERM_ALL; + + if (lua_isinteger (L, 2)) { + perms = luaL_checkinteger (L, 2); + } else if (lua_isstring (L, 2)) { + const gchar *perms_str = luaL_checkstring (L, 2); + if (!client_parse_permissions (perms_str, &perms)) + luaL_error (L, "invalid permission string: '%s'", perms_str); + } else { + luaL_error (L, "invalid permission argument"); + } + + wp_permission_manager_set_core_permissions (pm, perms); + return 0; } static int @@ -2726,6 +2746,7 @@ permission_manager_update_permissions (lua_State *L) static const luaL_Reg permission_manager_funcs[] = { { "set_default_permissions", permission_manager_set_default_permissions }, + { "set_core_permissions", permission_manager_set_core_permissions }, { "add_interest_match", permission_manager_add_interest_match }, { "add_interest_match_simple", permission_manager_add_interest_match_simple }, { "add_rules_match", permission_manager_add_rules_match }, diff --git a/src/config/wireplumber.conf.d.examples/access.conf b/src/config/wireplumber.conf.d.examples/access.conf index 4da8b0c9..da1a4d7d 100644 --- a/src/config/wireplumber.conf.d.examples/access.conf +++ b/src/config/wireplumber.conf.d.examples/access.conf @@ -12,6 +12,12 @@ access.permission-managers = [ # ## The default permissions to apply on all objects that dont have a match # default_permissions = "all" # + # ## The permissions to apply specifically on the PipeWire core object + # ## (ID 0). This is useful to allow clients to interact with the core + # ## (e.g. enumerate objects) while restricting access to individual objects. + # ## If not set, the default_permissions value is used for the core as well. + # core_permissions = "rx" + # # ## The rules to apply specific permissions to matched objects # rules = [ # { diff --git a/src/scripts/client/find-config-access.lua b/src/scripts/client/find-config-access.lua index ac83e4b0..835405ee 100644 --- a/src/scripts/client/find-config-access.lua +++ b/src/scripts/client/find-config-access.lua @@ -30,6 +30,11 @@ for _, pm_info in ipairs (config_pm_table) do config_pm:set_default_permissions (pm_info.default_permissions) end + -- Set core permissions if defined + if pm_info.core_permissions ~= nil then + config_pm:set_core_permissions (pm_info.core_permissions) + end + -- Set rules match if defined if pm_info.rules ~= nil then config_pm:add_rules_match (Json.Raw (pm_info.rules))