From 91ea0ee8b1ba877964d270b9202bc5b589dff938 Mon Sep 17 00:00:00 2001 From: Mauro <34335419+Velin92@users.noreply.github.com> Date: Mon, 17 Jul 2023 13:43:31 +0200 Subject: [PATCH] Add PowerLevels permission checks to room::Joined MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … and the FFI `Room`. These were previously already available at the `room::Member` level. --- bindings/matrix-sdk-ffi/src/room.rs | 89 +++++++++++++++++++++++- crates/matrix-sdk/src/room/joined/mod.rs | 85 +++++++++++++++++++--- 2 files changed, 164 insertions(+), 10 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index ea4fd4cf7..07e777aaf 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -35,7 +35,7 @@ use super::RUNTIME; use crate::{ client::ProgressWatcher, error::{ClientError, RoomError}, - room_member::RoomMember, + room_member::{MessageLikeEventType, RoomMember, StateEventType}, timeline::{ AudioInfo, FileInfo, ImageInfo, ThumbnailInfo, TimelineDiff, TimelineItem, TimelineListener, VideoInfo, @@ -68,7 +68,7 @@ impl Room { } } -#[uniffi::export] +#[uniffi::export(async_runtime = "tokio")] impl Room { pub fn id(&self) -> String { self.inner.room_id().to_string() @@ -902,6 +902,91 @@ impl Room { Ok(Arc::new(RoomMessageEventContent::new(msgtype))) }) } + + pub async fn can_user_redact(&self, user_id: String) -> Result { + let room = match &self.inner { + SdkRoom::Joined(j) => j.clone(), + _ => return Err(anyhow!("Can't check permissions for non joined rooms").into()), + }; + + let user_id = UserId::parse(&user_id)?; + Ok(room.can_user_redact(&user_id).await?) + } + + pub async fn can_user_ban(&self, user_id: String) -> Result { + let room = match &self.inner { + SdkRoom::Joined(j) => j.clone(), + _ => return Err(anyhow!("Can't check permissions for non joined rooms").into()), + }; + + let user_id = UserId::parse(&user_id)?; + Ok(room.can_user_ban(&user_id).await?) + } + + pub async fn can_user_invite(&self, user_id: String) -> Result { + let room = match &self.inner { + SdkRoom::Joined(j) => j.clone(), + _ => return Err(anyhow!("Can't check permissions for non joined rooms").into()), + }; + + let user_id = UserId::parse(&user_id)?; + Ok(room.can_user_invite(&user_id).await?) + } + + pub async fn can_user_kick(&self, user_id: String) -> Result { + let room = match &self.inner { + SdkRoom::Joined(j) => j.clone(), + _ => return Err(anyhow!("Can't check permissions for non joined rooms").into()), + }; + + let user_id = UserId::parse(&user_id)?; + Ok(room.can_user_kick(&user_id).await?) + } + + pub async fn can_user_send_state( + &self, + user_id: String, + state_event: StateEventType, + ) -> Result { + let room = match &self.inner { + SdkRoom::Joined(j) => j.clone(), + _ => return Err(anyhow!("Can't check permissions for non joined rooms").into()), + }; + + let user_id = UserId::parse(&user_id)?; + Ok(room.can_user_send_state(&user_id, state_event.into()).await?) + } + + pub async fn can_user_send_message( + &self, + user_id: String, + message: MessageLikeEventType, + ) -> Result { + let room = match &self.inner { + SdkRoom::Joined(j) => j.clone(), + _ => return Err(anyhow!("Can't check permissions for non joined rooms").into()), + }; + + let user_id = UserId::parse(&user_id)?; + Ok(room.can_user_send_message(&user_id, message.into()).await?) + } + + pub async fn can_user_trigger_room_notification( + &self, + user_id: String, + ) -> Result { + let room = match &self.inner { + SdkRoom::Joined(j) => j.clone(), + _ => return Err(anyhow!("Can't check permissions for non joined rooms").into()), + }; + + let user_id = UserId::parse(&user_id)?; + Ok(room.can_user_trigger_room_notification(&user_id).await?) + } + + pub fn own_user_id(&self) -> String { + self.inner.own_user_id().to_string() + } } impl Room { diff --git a/crates/matrix-sdk/src/room/joined/mod.rs b/crates/matrix-sdk/src/room/joined/mod.rs index 480954eb3..8dbb39a56 100644 --- a/crates/matrix-sdk/src/room/joined/mod.rs +++ b/crates/matrix-sdk/src/room/joined/mod.rs @@ -28,10 +28,11 @@ use ruma::{ avatar::{ImageInfo, RoomAvatarEventContent}, message::RoomMessageEventContent, name::RoomNameEventContent, - power_levels::RoomPowerLevelsEventContent, + power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, topic::RoomTopicEventContent, }, - EmptyStateKey, MessageLikeEventContent, StateEventContent, + EmptyStateKey, MessageLikeEventContent, MessageLikeEventType, StateEventContent, + StateEventType, }, serde::Raw, EventId, Int, MxcUri, OwnedEventId, OwnedTransactionId, TransactionId, UserId, @@ -811,12 +812,7 @@ impl Joined { &self, updates: Vec<(&UserId, Int)>, ) -> Result { - let raw_pl_event = self - .get_state_event_static::() - .await? - .ok_or(Error::InsufficientData)?; - - let mut power_levels = raw_pl_event.deserialize()?.power_levels(); + let mut power_levels = self.get_room_power_levels().await?; for (user_id, new_level) in updates { if new_level == power_levels.users_default { @@ -829,6 +825,15 @@ impl Joined { self.send_state_event(RoomPowerLevelsEventContent::from(power_levels)).await } + async fn get_room_power_levels(&self) -> Result { + Ok(self + .get_state_event_static::() + .await? + .ok_or(Error::InsufficientData)? + .deserialize()? + .power_levels()) + } + /// Sets the name of this room. pub async fn set_name(&self, name: Option) -> Result { self.send_state_event(RoomNameEventContent::new(name)).await @@ -1096,6 +1101,70 @@ impl Joined { self.client.send(request, None).await } + + /// Returns true if the user with the given user_id is able to redact + /// messages in the room. + /// + /// The call may fail if there is an error in getting the power levels. + pub async fn can_user_redact(&self, user_id: &UserId) -> Result { + Ok(self.get_room_power_levels().await?.user_can_redact(user_id)) + } + + /// Returns true if the user with the given user_id is able to ban in the + /// room. + /// + /// The call may fail if there is an error in getting the power levels. + pub async fn can_user_ban(&self, user_id: &UserId) -> Result { + Ok(self.get_room_power_levels().await?.user_can_ban(user_id)) + } + + /// Returns true if the user with the given user_id is able to kick in the + /// room. + /// + /// The call may fail if there is an error in getting the power levels. + pub async fn can_user_invite(&self, user_id: &UserId) -> Result { + Ok(self.get_room_power_levels().await?.user_can_invite(user_id)) + } + + /// Returns true if the user with the given user_id is able to kick in the + /// room. + /// + /// The call may fail if there is an error in getting the power levels. + pub async fn can_user_kick(&self, user_id: &UserId) -> Result { + Ok(self.get_room_power_levels().await?.user_can_kick(user_id)) + } + + /// Returns true if the user with the given user_id is able to send a + /// specific state event type in the room. + /// + /// The call may fail if there is an error in getting the power levels. + pub async fn can_user_send_state( + &self, + user_id: &UserId, + state_event: StateEventType, + ) -> Result { + Ok(self.get_room_power_levels().await?.user_can_send_state(user_id, state_event)) + } + + /// Returns true if the user with the given user_id is able to send a + /// specific message type in the room. + /// + /// The call may fail if there is an error in getting the power levels. + pub async fn can_user_send_message( + &self, + user_id: &UserId, + message: MessageLikeEventType, + ) -> Result { + Ok(self.get_room_power_levels().await?.user_can_send_message(user_id, message)) + } + + /// Returns true if the user with the given user_id is able to trigger a + /// notification in the room. + /// + /// The call may fail if there is an error in getting the power levels. + pub async fn can_user_trigger_room_notification(&self, user_id: &UserId) -> Result { + Ok(self.get_room_power_levels().await?.user_can_trigger_room_notification(user_id)) + } } /// Receipts to send all at once.