Skip to content

Commit

Permalink
✨ zb,zm: Support Option<Header<'_>> parameter in property getters
Browse files Browse the repository at this point in the history
This allows for fine-grained connecting-UID checking, in order to
be able to control who is able to access property values.

Signed-off-by: Razvan Cojocaru <[email protected]>
  • Loading branch information
rzvncj committed Dec 18, 2024
1 parent e2ec19b commit 088d643
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 102 deletions.
5 changes: 3 additions & 2 deletions zbus/src/fdo/object_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use zbus_names::{InterfaceName, OwnedInterfaceName};
use zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Value};

use super::{Error, Result};
use crate::{interface, message::Header, object_server::SignalEmitter, ObjectServer};
use crate::{interface, message::Header, object_server::SignalEmitter, Connection, ObjectServer};

/// The type returned by the [`ObjectManagerProxy::get_managed_objects`] method.
pub type ManagedObjects =
Expand Down Expand Up @@ -49,6 +49,7 @@ impl ObjectManager {
async fn get_managed_objects(
&self,
#[zbus(object_server)] server: &ObjectServer,
#[zbus(connection)] connection: &Connection,
#[zbus(header)] header: Header<'_>,
) -> Result<ManagedObjects> {
let path = header.path().ok_or(crate::Error::MissingField)?;
Expand All @@ -57,7 +58,7 @@ impl ObjectManager {
.get_child(path)
.ok_or_else(|| Error::UnknownObject(format!("Unknown object '{path}'")))?;

node.get_managed_objects().await
node.get_managed_objects(server, connection).await
}

/// This signal is emitted when either a new object is added or when an existing object gains
Expand Down
32 changes: 22 additions & 10 deletions zbus/src/fdo/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use zbus_names::InterfaceName;
use zvariant::{OwnedValue, Value};

use super::{Error, Result};
use crate::{interface, message::Header, object_server::SignalEmitter, ObjectServer};
use crate::{interface, message::Header, object_server::SignalEmitter, Connection, ObjectServer};

/// Service-side implementation for the `org.freedesktop.DBus.Properties` interface.
/// This interface is implemented automatically for any object registered to the
Expand All @@ -29,6 +29,7 @@ impl Properties {
&self,
interface_name: InterfaceName<'_>,
property_name: &str,
#[zbus(connection)] conn: &Connection,
#[zbus(object_server)] server: &ObjectServer,
#[zbus(header)] header: Header<'_>,
) -> Result<OwnedValue> {
Expand All @@ -41,7 +42,12 @@ impl Properties {
Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
})?;

let res = iface.instance.read().await.get(property_name).await;
let res = iface
.instance
.read()
.await
.get(property_name, server, conn, &Some(header))
.await;
res.unwrap_or_else(|| {
Err(Error::UnknownProperty(format!(
"Unknown property '{property_name}'"
Expand All @@ -68,12 +74,12 @@ impl Properties {
Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
})?;

match iface
.instance
.read()
.await
.set(property_name, &value, &emitter)
{
match iface.instance.read().await.set(
property_name,
&value,
&emitter,
&Some(header.clone()),
) {
zbus::object_server::DispatchResult::RequiresMut => {}
zbus::object_server::DispatchResult::NotFound => {
return Err(Error::UnknownProperty(format!(
Expand All @@ -88,7 +94,7 @@ impl Properties {
.instance
.write()
.await
.set_mut(property_name, &value, &emitter)
.set_mut(property_name, &value, &emitter, &Some(header))
.await;
res.unwrap_or_else(|| {
Err(Error::UnknownProperty(format!(
Expand All @@ -102,6 +108,7 @@ impl Properties {
&self,
interface_name: InterfaceName<'_>,
#[zbus(object_server)] server: &ObjectServer,
#[zbus(connection)] connection: &Connection,
#[zbus(header)] header: Header<'_>,
) -> Result<HashMap<String, OwnedValue>> {
let path = header.path().ok_or(crate::Error::MissingField)?;
Expand All @@ -113,7 +120,12 @@ impl Properties {
Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
})?;

let res = iface.instance.read().await.get_all().await?;
let res = iface
.instance
.read()
.await
.get_all(server, connection, &Some(header))
.await?;
Ok(res)
}

Expand Down
31 changes: 26 additions & 5 deletions zbus/src/object_server/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ use zbus_names::{InterfaceName, MemberName};
use zvariant::{OwnedValue, Value};

use crate::{
async_lock::RwLock, fdo, message::Message, object_server::SignalEmitter, Connection,
ObjectServer,
async_lock::RwLock,
fdo,
message::{self, Header, Message},
object_server::SignalEmitter,
Connection, ObjectServer,
};

/// This trait is used to dispatch messages to an interface instance.
Expand All @@ -45,10 +48,26 @@ pub trait Interface: Any + Send + Sync {
}

/// Get a property value. Returns `None` if the property doesn't exist.
async fn get(&self, property_name: &str) -> Option<fdo::Result<OwnedValue>>;
///
/// Note: The header parameter will be None when the getter is not being called as part
/// of D-Bus communication (for example, when it is called as part of initial object setup,
/// before it is registered on the bus, or when we manually send out property changed
/// notifications).
async fn get(
&self,
property_name: &str,
server: &ObjectServer,
connection: &Connection,
header: &Option<message::Header<'_>>,
) -> Option<fdo::Result<OwnedValue>>;

/// Return all the properties.
async fn get_all(&self) -> fdo::Result<HashMap<String, OwnedValue>>;
async fn get_all(
&self,
object_server: &ObjectServer,
connection: &Connection,
header: &Option<message::Header<'_>>,
) -> fdo::Result<HashMap<String, OwnedValue>>;

/// Set a property value.
///
Expand All @@ -60,8 +79,9 @@ pub trait Interface: Any + Send + Sync {
property_name: &'call str,
value: &'call Value<'_>,
emitter: &'call SignalEmitter<'_>,
header: &'call Option<message::Header<'_>>,
) -> DispatchResult<'call> {
let _ = (property_name, value, emitter);
let _ = (property_name, value, emitter, header);
DispatchResult::RequiresMut
}

Expand All @@ -75,6 +95,7 @@ pub trait Interface: Any + Send + Sync {
property_name: &str,
value: &Value<'_>,
emitter: &SignalEmitter<'_>,
header: &Option<Header<'_>>,
) -> Option<fdo::Result<()>>;

/// Call a method.
Expand Down
6 changes: 4 additions & 2 deletions zbus/src/object_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl ObjectServer {
if name == ObjectManager::name() {
// Just added an object manager. Need to signal all managed objects under it.
let ctxt = SignalEmitter::new(&self.connection(), path)?;
let objects = node.get_managed_objects().await?;
let objects = node.get_managed_objects(self, &self.connection()).await?;
for (path, owned_interfaces) in objects {
let interfaces = owned_interfaces
.iter()
Expand All @@ -165,7 +165,9 @@ impl ObjectServer {
} else if let Some(manager_path) = manager_path {
let ctxt = SignalEmitter::new(&self.connection(), manager_path.clone())?;
let mut interfaces = HashMap::new();
let owned_props = node.get_properties(name.clone()).await?;
let owned_props = node
.get_properties(self, &self.connection(), name.clone())
.await?;
let props = owned_props
.iter()
.map(|(k, v)| Ok((k.as_str(), Value::try_from(v)?)))
Expand Down
19 changes: 15 additions & 4 deletions zbus/src/object_server/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ use std::{
use zbus_names::InterfaceName;
use zvariant::{ObjectPath, OwnedObjectPath, OwnedValue};

use crate::fdo::{self, Introspectable, ManagedObjects, ObjectManager, Peer, Properties};
use crate::{
fdo::{self, Introspectable, ManagedObjects, ObjectManager, Peer, Properties},
Connection, ObjectServer,
};

use super::{ArcInterface, Interface};

Expand Down Expand Up @@ -208,7 +211,11 @@ impl Node {
xml
}

pub(crate) async fn get_managed_objects(&self) -> fdo::Result<ManagedObjects> {
pub(crate) async fn get_managed_objects(
&self,
object_server: &ObjectServer,
connection: &Connection,
) -> fdo::Result<ManagedObjects> {
let mut managed_objects = ManagedObjects::new();

// Recursively get all properties of all interfaces of descendants.
Expand All @@ -222,7 +229,9 @@ impl Node {
&& *n != &Properties::name()
&& *n != &ObjectManager::name()
}) {
let props = node.get_properties(iface_name.clone()).await?;
let props = node
.get_properties(object_server, connection, iface_name.clone())
.await?;
interfaces.insert(iface_name.clone().into(), props);
}
managed_objects.insert(node.path.clone(), interfaces);
Expand All @@ -234,14 +243,16 @@ impl Node {

pub(super) async fn get_properties(
&self,
object_server: &ObjectServer,
connection: &Connection,
interface_name: InterfaceName<'_>,
) -> fdo::Result<HashMap<String, OwnedValue>> {
self.interface_lock(interface_name)
.expect("Interface was added but not found")
.instance
.read()
.await
.get_all()
.get_all(object_server, connection, &None)
.await
}
}
16 changes: 16 additions & 0 deletions zbus/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,21 @@ impl MyIface {
self.count
}

#[instrument]
#[zbus(property)]
fn test_header_prop(
&self,
#[zbus(header)] header: Option<Header<'_>>,
#[zbus(connection)] connection: &Connection,
#[zbus(object_server)] object_server: &ObjectServer,
) -> bool {
debug!(
"`TestHeaderProp` getter called, header: {:?}, connection: {:?}, object_server: {:?}",
header, connection, object_server
);
header.is_some()
}

#[instrument]
#[zbus(property)]
async fn hash_map(&self) -> HashMap<String, String> {
Expand Down Expand Up @@ -570,6 +585,7 @@ async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result<u32> {
drop(props_changed_stream);

proxy.ping().await?;
assert_eq!(proxy.test_header_prop().await?, true);
assert_eq!(proxy.count().await?, 1);
assert_eq!(proxy.cached_count()?, None);

Expand Down
Loading

0 comments on commit 088d643

Please sign in to comment.