1
0
Fork 0
mirror of https://gitlab.com/famedly/conduit.git synced 2025-08-11 17:50:59 +00:00

feat: MSC4289, Explicitly privilege room creators (1/2)

This commit is contained in:
Matthias Ahouansou 2025-07-03 12:41:16 +01:00
parent be867db3d9
commit b5e318561c
No known key found for this signature in database
7 changed files with 282 additions and 227 deletions

22
Cargo.lock generated
View file

@ -2521,7 +2521,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma" name = "ruma"
version = "0.12.5" version = "0.12.5"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"assign", "assign",
"js_int", "js_int",
@ -2540,7 +2540,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-appservice-api" name = "ruma-appservice-api"
version = "0.12.2" version = "0.12.2"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"js_int", "js_int",
"ruma-common", "ruma-common",
@ -2552,7 +2552,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-client-api" name = "ruma-client-api"
version = "0.20.4" version = "0.20.4"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"as_variant", "as_variant",
"assign", "assign",
@ -2575,7 +2575,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-common" name = "ruma-common"
version = "0.15.4" version = "0.15.4"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"as_variant", "as_variant",
"base64 0.22.1", "base64 0.22.1",
@ -2607,7 +2607,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-events" name = "ruma-events"
version = "0.30.4" version = "0.30.4"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"as_variant", "as_variant",
"indexmap 2.9.0", "indexmap 2.9.0",
@ -2631,7 +2631,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-federation-api" name = "ruma-federation-api"
version = "0.11.2" version = "0.11.2"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"bytes", "bytes",
"headers", "headers",
@ -2653,7 +2653,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-identifiers-validation" name = "ruma-identifiers-validation"
version = "0.10.1" version = "0.10.1"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"js_int", "js_int",
"thiserror 2.0.12", "thiserror 2.0.12",
@ -2662,7 +2662,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-macros" name = "ruma-macros"
version = "0.15.2" version = "0.15.2"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"proc-macro-crate", "proc-macro-crate",
@ -2677,7 +2677,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-push-gateway-api" name = "ruma-push-gateway-api"
version = "0.11.0" version = "0.11.0"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"js_int", "js_int",
"ruma-common", "ruma-common",
@ -2689,7 +2689,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-signatures" name = "ruma-signatures"
version = "0.17.1" version = "0.17.1"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"ed25519-dalek", "ed25519-dalek",
@ -2705,7 +2705,7 @@ dependencies = [
[[package]] [[package]]
name = "ruma-state-res" name = "ruma-state-res"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68" source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
dependencies = [ dependencies = [
"js_int", "js_int",
"ruma-common", "ruma-common",

View file

@ -23,11 +23,16 @@ use ruma::{
}, },
int, int,
serde::JsonObject, serde::JsonObject,
CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId, CanonicalJsonObject, CanonicalJsonValue, OwnedRoomAliasId, OwnedUserId, RoomAliasId, RoomId,
}; };
use serde::Deserialize;
use serde_json::{json, value::to_raw_value}; use serde_json::{json, value::to_raw_value};
use std::{cmp::max, collections::BTreeMap, sync::Arc}; use std::{
use tracing::{info, warn}; cmp::max,
collections::{BTreeMap, HashSet},
sync::Arc,
};
use tracing::{error, info, warn};
/// # `POST /_matrix/client/r0/createRoom` /// # `POST /_matrix/client/r0/createRoom`
/// ///
@ -142,9 +147,33 @@ pub async fn create_room_route(
.expect("Supported room version must have rules.") .expect("Supported room version must have rules.")
.authorization; .authorization;
let mut users = BTreeMap::new();
if !rules.explicitly_privilege_room_creators {
users.insert(sender_user.clone(), int!(100));
}
// Figure out preset. We need it for preset specific events
let preset = body.preset.clone().unwrap_or(match &body.visibility {
room::Visibility::Private => RoomPreset::PrivateChat,
room::Visibility::Public => RoomPreset::PublicChat,
_ => RoomPreset::PrivateChat, // Room visibility should not be custom
});
let mut additional_creators: HashSet<OwnedUserId, _> = HashSet::new();
if preset == RoomPreset::TrustedPrivateChat {
if rules.additional_room_creators {
additional_creators.extend(body.invite.clone())
} else {
for invited_user in &body.invite {
users.insert(invited_user.clone(), int!(100));
}
}
}
let content = match &body.creation_content { let content = match &body.creation_content {
Some(content) => { Some(raw_content) => {
let mut content = content let mut content = raw_content
.deserialize_as_unchecked::<CanonicalJsonObject>() .deserialize_as_unchecked::<CanonicalJsonObject>()
.expect("Invalid creation content"); .expect("Invalid creation content");
@ -157,6 +186,27 @@ pub async fn create_room_route(
); );
} }
if rules.additional_room_creators && !additional_creators.is_empty() {
#[derive(Deserialize)]
struct AdditionalCreators {
additional_creators: Vec<OwnedUserId>,
}
if let Ok(AdditionalCreators {
additional_creators: ac,
}) = raw_content.deserialize_as_unchecked()
{
additional_creators.extend(ac);
}
content.insert(
"additional_creators".into(),
json!(&additional_creators).try_into().map_err(|_| {
Error::BadRequest(ErrorKind::BadJson, "Invalid additional creators")
})?,
);
}
content.insert( content.insert(
"room_version".into(), "room_version".into(),
json!(room_version.as_str()).try_into().map_err(|_| { json!(room_version.as_str()).try_into().map_err(|_| {
@ -166,24 +216,22 @@ pub async fn create_room_route(
content content
} }
None => { None => {
let content = if rules.use_room_create_sender { let content = RoomCreateEventContent {
additional_creators: additional_creators.into_iter().collect(),
room_version,
..if rules.use_room_create_sender {
RoomCreateEventContent::new_v11() RoomCreateEventContent::new_v11()
} else { } else {
RoomCreateEventContent::new_v1(sender_user.clone()) RoomCreateEventContent::new_v1(sender_user.clone())
}
}; };
let mut content = serde_json::from_str::<CanonicalJsonObject>(
serde_json::from_str::<CanonicalJsonObject>(
to_raw_value(&content) to_raw_value(&content)
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))? .map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
.get(), .get(),
) )
.unwrap(); .expect("room create event content created by us is valid")
content.insert(
"room_version".into(),
json!(room_version.as_str()).try_into().map_err(|_| {
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
})?,
);
content
} }
}; };
@ -250,26 +298,9 @@ pub async fn create_room_route(
.await?; .await?;
// 3. Power levels // 3. Power levels
// Figure out preset. We need it for preset specific events
let preset = body.preset.clone().unwrap_or(match &body.visibility {
room::Visibility::Private => RoomPreset::PrivateChat,
room::Visibility::Public => RoomPreset::PublicChat,
_ => RoomPreset::PrivateChat, // Room visibility should not be custom
});
let mut users = BTreeMap::new();
users.insert(sender_user.clone(), int!(100));
if preset == RoomPreset::TrustedPrivateChat {
for invite_ in &body.invite {
users.insert(invite_.clone(), int!(100));
}
}
let mut power_levels_content = serde_json::to_value(RoomPowerLevelsEventContent { let mut power_levels_content = serde_json::to_value(RoomPowerLevelsEventContent {
users, users,
..Default::default() ..RoomPowerLevelsEventContent::new(&rules)
}) })
.expect("event is valid, we just created it"); .expect("event is valid, we just created it");
@ -587,7 +618,8 @@ pub async fn upgrade_room_route(
let rules = body let rules = body
.new_version .new_version
.rules() .rules()
.expect("Supported room version must have rules."); .expect("Supported room version must have rules.")
.authorization;
// Create a replacement room // Create a replacement room
let replacement_room = RoomId::new(services().globals.server_name()); let replacement_room = RoomId::new(services().globals.server_name());
@ -663,7 +695,7 @@ pub async fn upgrade_room_route(
)); ));
// Send a m.room.create event containing a predecessor field and the applicable room_version // Send a m.room.create event containing a predecessor field and the applicable room_version
if rules.authorization.use_room_create_sender { if rules.use_room_create_sender {
create_event_content.remove("creator"); create_event_content.remove("creator");
} else { } else {
create_event_content.insert( create_event_content.insert(
@ -674,6 +706,18 @@ pub async fn upgrade_room_route(
); );
} }
if rules.additional_room_creators && !body.additional_creators.is_empty() {
create_event_content.insert(
"additional_creators".into(),
json!(&body.additional_creators).try_into().map_err(|_| {
Error::BadRequest(
ErrorKind::BadJson,
"Failed to convert provided additional additional creators to JSON",
)
})?,
);
}
create_event_content.insert( create_event_content.insert(
"room_version".into(), "room_version".into(),
json!(&body.new_version) json!(&body.new_version)
@ -764,7 +808,7 @@ pub async fn upgrade_room_route(
// Replicate transferable state events to the new room // Replicate transferable state events to the new room
for event_type in transferable_state_events { for event_type in transferable_state_events {
let event_content = let mut event_content =
match services() match services()
.rooms .rooms
.state_accessor .state_accessor
@ -774,6 +818,31 @@ pub async fn upgrade_room_route(
None => continue, // Skipping missing events. None => continue, // Skipping missing events.
}; };
if event_type == StateEventType::RoomPowerLevels && rules.explicitly_privilege_room_creators
{
let mut pl_event_content: CanonicalJsonObject =
serde_json::from_str(event_content.get()).map_err(|e| {
error!(
"Invalid m.room.power_levels event content in room {}: {e}",
body.room_id
);
Error::BadDatabase("Invalid m.room.power_levels event content in room")
})?;
if let Some(CanonicalJsonValue::Object(users)) = pl_event_content.get_mut("users") {
users.remove(sender_user.as_str());
if rules.additional_room_creators {
for user in &body.additional_creators {
users.remove(user.as_str());
}
}
}
event_content = to_raw_value(&pl_event_content)
.expect("Must serialize, only changes made was removing keys")
}
services() services()
.rooms .rooms
.timeline .timeline

View file

@ -1756,7 +1756,9 @@ impl Service {
// 3. Power levels // 3. Power levels
let mut users = BTreeMap::new(); let mut users = BTreeMap::new();
if !rules.explicitly_privilege_room_creators {
users.insert(conduit_user.to_owned(), 100.into()); users.insert(conduit_user.to_owned(), 100.into());
}
services() services()
.rooms .rooms
@ -1766,7 +1768,7 @@ impl Service {
event_type: TimelineEventType::RoomPowerLevels, event_type: TimelineEventType::RoomPowerLevels,
content: to_raw_value(&RoomPowerLevelsEventContent { content: to_raw_value(&RoomPowerLevelsEventContent {
users, users,
..Default::default() ..RoomPowerLevelsEventContent::new(&rules)
}) })
.expect("event is valid, we just created it"), .expect("event is valid, we just created it"),
unsigned: None, unsigned: None,
@ -2010,8 +2012,16 @@ impl Service {
.await?; .await?;
// Set power level // Set power level
let room_version = services().rooms.state.get_room_version(&room_id)?;
let rules = room_version
.rules()
.expect("Supported room version must have rules.")
.authorization;
let mut users = BTreeMap::new(); let mut users = BTreeMap::new();
if !rules.explicitly_privilege_room_creators {
users.insert(conduit_user.to_owned(), 100.into()); users.insert(conduit_user.to_owned(), 100.into());
}
users.insert(user_id.to_owned(), 100.into()); users.insert(user_id.to_owned(), 100.into());
services() services()
@ -2022,7 +2032,7 @@ impl Service {
event_type: TimelineEventType::RoomPowerLevels, event_type: TimelineEventType::RoomPowerLevels,
content: to_raw_value(&RoomPowerLevelsEventContent { content: to_raw_value(&RoomPowerLevelsEventContent {
users, users,
..Default::default() ..RoomPowerLevelsEventContent::new(&rules)
}) })
.expect("event is valid, we just created it"), .expect("event is valid, we just created it"),
unsigned: None, unsigned: None,

View file

@ -13,7 +13,7 @@ use ruma::{
}, },
IncomingResponse, OutgoingRequest, SendAccessToken, IncomingResponse, OutgoingRequest, SendAccessToken,
}, },
events::{room::power_levels::RoomPowerLevelsEventContent, StateEventType, TimelineEventType}, events::TimelineEventType,
push::{Action, PushConditionRoomCtx, PushFormat, Ruleset, Tweak}, push::{Action, PushConditionRoomCtx, PushFormat, Ruleset, Tweak},
serde::Raw, serde::Raw,
uint, RoomId, UInt, UserId, uint, RoomId, UInt, UserId,
@ -139,21 +139,12 @@ impl Service {
let mut notify = None; let mut notify = None;
let mut tweaks = Vec::new(); let mut tweaks = Vec::new();
let power_levels: RoomPowerLevelsEventContent = services() let power_levels = services().rooms.state_accessor.power_levels(&pdu.room_id)?;
.rooms
.state_accessor
.room_state_get(&pdu.room_id, &StateEventType::RoomPowerLevels, "")?
.map(|ev| {
serde_json::from_str(ev.content.get())
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
})
.transpose()?
.unwrap_or_default();
for action in self.get_actions( for action in self.get_actions(
user, user,
&ruleset, &ruleset,
&power_levels, power_levels.into(),
&pdu.to_sync_room_event(), &pdu.to_sync_room_event(),
&pdu.room_id, &pdu.room_id,
)? { )? {
@ -188,16 +179,10 @@ impl Service {
&self, &self,
user: &UserId, user: &UserId,
ruleset: &'a Ruleset, ruleset: &'a Ruleset,
power_levels: &RoomPowerLevelsEventContent, power_levels: PushConditionPowerLevelsCtx,
pdu: &Raw<AnySyncTimelineEvent>, pdu: &Raw<AnySyncTimelineEvent>,
room_id: &RoomId, room_id: &RoomId,
) -> Result<&'a [Action]> { ) -> Result<&'a [Action]> {
let power_levels = PushConditionPowerLevelsCtx {
users: power_levels.users.clone(),
users_default: power_levels.users_default,
notifications: power_levels.notifications.clone(),
};
let ctx = PushConditionRoomCtx { let ctx = PushConditionRoomCtx {
room_id: room_id.to_owned(), room_id: room_id.to_owned(),
member_count: 10_u32.into(), // TODO: get member count efficiently member_count: 10_u32.into(), // TODO: get member count efficiently

View file

@ -2,7 +2,6 @@ mod data;
pub use data::Data; pub use data::Data;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use tracing::error;
use crate::{services, Error, Result}; use crate::{services, Error, Result};
use ruma::{ use ruma::{
@ -11,10 +10,7 @@ use ruma::{
client::{alias::get_alias, error::ErrorKind}, client::{alias::get_alias, error::ErrorKind},
federation, federation,
}, },
events::{ events::StateEventType,
room::power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
StateEventType,
},
OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, UserId, OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, UserId,
}; };
@ -55,27 +51,14 @@ impl Service {
{ {
Ok(true) Ok(true)
// Checking whether the user is able to change canonical aliases of the room // Checking whether the user is able to change canonical aliases of the room
} else if let Some(event) = services().rooms.state_accessor.room_state_get(
&room_id,
&StateEventType::RoomPowerLevels,
"",
)? {
serde_json::from_str(event.content.get())
.map_err(|_| Error::bad_database("Invalid event content for m.room.power_levels"))
.map(|content: RoomPowerLevelsEventContent| {
RoomPowerLevels::from(content)
.user_can_send_state(user_id, StateEventType::RoomCanonicalAlias)
})
// If there is no power levels event, only the room creator can change canonical aliases
} else if let Some(event) = services().rooms.state_accessor.room_state_get(
&room_id,
&StateEventType::RoomCreate,
"",
)? {
Ok(event.sender == user_id)
} else { } else {
error!("Room {} has no m.room.create event (VERY BAD)!", room_id); services()
Err(Error::bad_database("Room has no m.room.create event")) .rooms
.state_accessor
.power_levels(&room_id)
.map(|power_levels| {
power_levels.user_can_send_state(user_id, StateEventType::RoomCanonicalAlias)
})
} }
} }

View file

@ -20,7 +20,7 @@ use ruma::{
StateEventType, StateEventType,
}, },
room::{JoinRuleSummary, RoomMembership}, room::{JoinRuleSummary, RoomMembership},
state_res::Event, state_res::{events::RoomCreateEvent, Event},
EventId, JsOption, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, EventId, JsOption, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
}; };
use serde_json::value::to_raw_value; use serde_json::value::to_raw_value;
@ -361,15 +361,10 @@ impl Service {
room_id: &RoomId, room_id: &RoomId,
federation: bool, federation: bool,
) -> Result<bool> { ) -> Result<bool> {
self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")? self.power_levels(room_id).map(|power_levels| {
.map(|e| { power_levels.user_can_redact_event_of_other(sender)
serde_json::from_str(e.content.get()) || power_levels.user_can_redact_own_event(sender)
.map(|c: RoomPowerLevelsEventContent| c.into()) && if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) {
.map(|e: RoomPowerLevels| {
e.user_can_redact_event_of_other(sender)
|| e.user_can_redact_own_event(sender)
&& if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts)
{
if federation { if federation {
pdu.sender().server_name() == sender.server_name() pdu.sender().server_name() == sender.server_name()
} else { } else {
@ -379,25 +374,6 @@ impl Service {
false false
} }
}) })
.map_err(|_| {
Error::bad_database("Invalid m.room.power_levels event in database")
})
})
// Falling back on m.room.create to judge power levels
.unwrap_or_else(|| {
if let Some(pdu) = self.room_state_get(room_id, &StateEventType::RoomCreate, "")? {
Ok(pdu.sender == sender
|| if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) {
pdu.sender == sender
} else {
false
})
} else {
Err(Error::bad_database(
"No m.room.power_levels or m.room.create events in database for room",
))
}
})
} }
/// Checks if guests are able to join a given room /// Checks if guests are able to join a given room
@ -485,4 +461,42 @@ impl Service {
} }
room_ids room_ids
} }
/// Gets the effective power levels of a room, regardless of if there is an
/// `m.rooms.power_levels` state.
pub fn power_levels(&self, room_id: &RoomId) -> Result<RoomPowerLevels> {
let power_levels: Option<RoomPowerLevelsEventContent> = self
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
.map(|ev| {
serde_json::from_str(ev.content.get())
.map_err(|_| Error::bad_database("invalid m.room.power_levels event."))
})
.transpose()?;
let room_create = self
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
.map(RoomCreateEvent::new)
.ok_or_else(|| Error::bad_database("Found room without m.room.create event."))?;
let room_version = room_create.room_version().map_err(|e| {
error!("Invalid room version in room create content for room {room_id}: {e}");
Error::BadDatabase("Room Create content had invalid room version")
})?;
let rules = room_version
.rules()
.expect("Supported room version must have rules.")
.authorization;
room_create
// NOTE: This is because project hydra's client-side was made public before the server
// side. This will be fixed by using `creators` instead in part 2/2.
.creator(&rules)
.map_err(|e| {
error!("Failed to get creators of room id {}: {e}", room_id);
Error::BadDatabase("RoomCreateEvent has invalid creators")
})
.map(|creator| {
RoomPowerLevels::new(power_levels.into(), &rules, [creator.into_owned()])
})
}
} }

View file

@ -15,12 +15,11 @@ use ruma::{
push_rules::PushRulesEvent, push_rules::PushRulesEvent,
room::{ room::{
canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent, canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent,
encrypted::Relation, member::MembershipState, encrypted::Relation, member::MembershipState, redaction::RoomRedactionEventContent,
power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent,
}, },
GlobalAccountDataEventType, StateEventType, TimelineEventType, GlobalAccountDataEventType, StateEventType, TimelineEventType,
}, },
push::{Action, Ruleset, Tweak}, push::{Action, PushConditionPowerLevelsCtx, Ruleset, Tweak},
state_res::{self, Event}, state_res::{self, Event},
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch,
OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId, OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId,
@ -290,17 +289,18 @@ impl Service {
drop(insert_lock); drop(insert_lock);
// See if the event matches any known pushers // See if the event matches any known pushers
let power_levels: RoomPowerLevelsEventContent = services() //
// Will fail if this is the first event (the create event), which is fine, since it cannot
// notify anyone else anyways
if pdu.kind != TimelineEventType::RoomCreate
|| pdu.state_key.as_deref().is_none_or(|key| !key.is_empty())
{
if let Ok(power_levels) = services()
.rooms .rooms
.state_accessor .state_accessor
.room_state_get(&pdu.room_id, &StateEventType::RoomPowerLevels, "")? .power_levels(&pdu.room_id)
.map(|ev| { .map(PushConditionPowerLevelsCtx::from)
serde_json::from_str(ev.content.get()) {
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
})
.transpose()?
.unwrap_or_default();
let sync_pdu = pdu.to_sync_room_event(); let sync_pdu = pdu.to_sync_room_event();
let mut notifies = Vec::new(); let mut notifies = Vec::new();
@ -351,7 +351,7 @@ impl Service {
for action in services().pusher.get_actions( for action in services().pusher.get_actions(
user, user,
&rules_for_user, &rules_for_user,
&power_levels, power_levels.clone(),
&sync_pdu, &sync_pdu,
&pdu.room_id, &pdu.room_id,
)? { )? {
@ -379,6 +379,8 @@ impl Service {
self.db self.db
.increment_notification_counts(&pdu.room_id, notifies, highlights)?; .increment_notification_counts(&pdu.room_id, notifies, highlights)?;
}
}
match pdu.kind { match pdu.kind {
TimelineEventType::RoomRedaction => { TimelineEventType::RoomRedaction => {
@ -1167,16 +1169,8 @@ impl Service {
return Ok(()); return Ok(());
} }
let power_levels: RoomPowerLevelsEventContent = services() let power_levels = services().rooms.state_accessor.power_levels(room_id)?;
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
.map(|ev| {
serde_json::from_str(ev.content.get())
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
})
.transpose()?
.unwrap_or_default();
let mut admin_servers = power_levels let mut admin_servers = power_levels
.users .users
.iter() .iter()