Merge branch 'master' into 'master'

permission-manager: Add core_permissions support

See merge request pipewire/wireplumber!822
This commit is contained in:
Torkel Niklasson 2026-04-29 06:23:02 +00:00
commit df848522e0
9 changed files with 159 additions and 2 deletions

View file

@ -58,3 +58,68 @@ 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"
core_permissions = "rx"
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``)
* ``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
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.

View file

@ -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

View file

@ -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',

View file

@ -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:

View file

@ -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)

View file

@ -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,

View file

@ -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 },

View file

@ -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 = [
# {

View file

@ -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))