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:
parent
be867db3d9
commit
b5e318561c
7 changed files with 282 additions and 227 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
@ -2521,7 +2521,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma"
|
||||
version = "0.12.5"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"assign",
|
||||
"js_int",
|
||||
|
@ -2540,7 +2540,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-appservice-api"
|
||||
version = "0.12.2"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
|
@ -2552,7 +2552,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-client-api"
|
||||
version = "0.20.4"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"assign",
|
||||
|
@ -2575,7 +2575,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-common"
|
||||
version = "0.15.4"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"base64 0.22.1",
|
||||
|
@ -2607,7 +2607,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-events"
|
||||
version = "0.30.4"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"as_variant",
|
||||
"indexmap 2.9.0",
|
||||
|
@ -2631,7 +2631,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-federation-api"
|
||||
version = "0.11.2"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"headers",
|
||||
|
@ -2653,7 +2653,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-identifiers-validation"
|
||||
version = "0.10.1"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"thiserror 2.0.12",
|
||||
|
@ -2662,7 +2662,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-macros"
|
||||
version = "0.15.2"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"proc-macro-crate",
|
||||
|
@ -2677,7 +2677,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-push-gateway-api"
|
||||
version = "0.11.0"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
|
@ -2689,7 +2689,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-signatures"
|
||||
version = "0.17.1"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"ed25519-dalek",
|
||||
|
@ -2705,7 +2705,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ruma-state-res"
|
||||
version = "0.13.0"
|
||||
source = "git+https://github.com/ruma/ruma.git#7bae3d0c0b8edf008899ac2b04cf9722182eef68"
|
||||
source = "git+https://github.com/ruma/ruma.git#71069c43650d2a797181aaa2ecc63037eaece907"
|
||||
dependencies = [
|
||||
"js_int",
|
||||
"ruma-common",
|
||||
|
|
|
@ -23,11 +23,16 @@ use ruma::{
|
|||
},
|
||||
int,
|
||||
serde::JsonObject,
|
||||
CanonicalJsonObject, OwnedRoomAliasId, RoomAliasId, RoomId,
|
||||
CanonicalJsonObject, CanonicalJsonValue, OwnedRoomAliasId, OwnedUserId, RoomAliasId, RoomId,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde_json::{json, value::to_raw_value};
|
||||
use std::{cmp::max, collections::BTreeMap, sync::Arc};
|
||||
use tracing::{info, warn};
|
||||
use std::{
|
||||
cmp::max,
|
||||
collections::{BTreeMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
/// # `POST /_matrix/client/r0/createRoom`
|
||||
///
|
||||
|
@ -142,9 +147,33 @@ pub async fn create_room_route(
|
|||
.expect("Supported room version must have rules.")
|
||||
.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 {
|
||||
Some(content) => {
|
||||
let mut content = content
|
||||
Some(raw_content) => {
|
||||
let mut content = raw_content
|
||||
.deserialize_as_unchecked::<CanonicalJsonObject>()
|
||||
.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(
|
||||
"room_version".into(),
|
||||
json!(room_version.as_str()).try_into().map_err(|_| {
|
||||
|
@ -166,24 +216,22 @@ pub async fn create_room_route(
|
|||
content
|
||||
}
|
||||
None => {
|
||||
let content = if rules.use_room_create_sender {
|
||||
RoomCreateEventContent::new_v11()
|
||||
} else {
|
||||
RoomCreateEventContent::new_v1(sender_user.clone())
|
||||
let content = RoomCreateEventContent {
|
||||
additional_creators: additional_creators.into_iter().collect(),
|
||||
room_version,
|
||||
..if rules.use_room_create_sender {
|
||||
RoomCreateEventContent::new_v11()
|
||||
} else {
|
||||
RoomCreateEventContent::new_v1(sender_user.clone())
|
||||
}
|
||||
};
|
||||
let mut content = serde_json::from_str::<CanonicalJsonObject>(
|
||||
|
||||
serde_json::from_str::<CanonicalJsonObject>(
|
||||
to_raw_value(&content)
|
||||
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Invalid creation content"))?
|
||||
.get(),
|
||||
)
|
||||
.unwrap();
|
||||
content.insert(
|
||||
"room_version".into(),
|
||||
json!(room_version.as_str()).try_into().map_err(|_| {
|
||||
Error::BadRequest(ErrorKind::BadJson, "Invalid creation content")
|
||||
})?,
|
||||
);
|
||||
content
|
||||
.expect("room create event content created by us is valid")
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -250,26 +298,9 @@ pub async fn create_room_route(
|
|||
.await?;
|
||||
|
||||
// 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 {
|
||||
users,
|
||||
..Default::default()
|
||||
..RoomPowerLevelsEventContent::new(&rules)
|
||||
})
|
||||
.expect("event is valid, we just created it");
|
||||
|
||||
|
@ -587,7 +618,8 @@ pub async fn upgrade_room_route(
|
|||
let rules = body
|
||||
.new_version
|
||||
.rules()
|
||||
.expect("Supported room version must have rules.");
|
||||
.expect("Supported room version must have rules.")
|
||||
.authorization;
|
||||
|
||||
// Create a replacement room
|
||||
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
|
||||
if rules.authorization.use_room_create_sender {
|
||||
if rules.use_room_create_sender {
|
||||
create_event_content.remove("creator");
|
||||
} else {
|
||||
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(
|
||||
"room_version".into(),
|
||||
json!(&body.new_version)
|
||||
|
@ -764,7 +808,7 @@ pub async fn upgrade_room_route(
|
|||
|
||||
// Replicate transferable state events to the new room
|
||||
for event_type in transferable_state_events {
|
||||
let event_content =
|
||||
let mut event_content =
|
||||
match services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
|
@ -774,6 +818,31 @@ pub async fn upgrade_room_route(
|
|||
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()
|
||||
.rooms
|
||||
.timeline
|
||||
|
|
|
@ -1756,7 +1756,9 @@ impl Service {
|
|||
|
||||
// 3. Power levels
|
||||
let mut users = BTreeMap::new();
|
||||
users.insert(conduit_user.to_owned(), 100.into());
|
||||
if !rules.explicitly_privilege_room_creators {
|
||||
users.insert(conduit_user.to_owned(), 100.into());
|
||||
}
|
||||
|
||||
services()
|
||||
.rooms
|
||||
|
@ -1766,7 +1768,7 @@ impl Service {
|
|||
event_type: TimelineEventType::RoomPowerLevels,
|
||||
content: to_raw_value(&RoomPowerLevelsEventContent {
|
||||
users,
|
||||
..Default::default()
|
||||
..RoomPowerLevelsEventContent::new(&rules)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
|
@ -2010,8 +2012,16 @@ impl Service {
|
|||
.await?;
|
||||
|
||||
// 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();
|
||||
users.insert(conduit_user.to_owned(), 100.into());
|
||||
if !rules.explicitly_privilege_room_creators {
|
||||
users.insert(conduit_user.to_owned(), 100.into());
|
||||
}
|
||||
users.insert(user_id.to_owned(), 100.into());
|
||||
|
||||
services()
|
||||
|
@ -2022,7 +2032,7 @@ impl Service {
|
|||
event_type: TimelineEventType::RoomPowerLevels,
|
||||
content: to_raw_value(&RoomPowerLevelsEventContent {
|
||||
users,
|
||||
..Default::default()
|
||||
..RoomPowerLevelsEventContent::new(&rules)
|
||||
})
|
||||
.expect("event is valid, we just created it"),
|
||||
unsigned: None,
|
||||
|
|
|
@ -13,7 +13,7 @@ use ruma::{
|
|||
},
|
||||
IncomingResponse, OutgoingRequest, SendAccessToken,
|
||||
},
|
||||
events::{room::power_levels::RoomPowerLevelsEventContent, StateEventType, TimelineEventType},
|
||||
events::TimelineEventType,
|
||||
push::{Action, PushConditionRoomCtx, PushFormat, Ruleset, Tweak},
|
||||
serde::Raw,
|
||||
uint, RoomId, UInt, UserId,
|
||||
|
@ -139,21 +139,12 @@ impl Service {
|
|||
let mut notify = None;
|
||||
let mut tweaks = Vec::new();
|
||||
|
||||
let power_levels: RoomPowerLevelsEventContent = services()
|
||||
.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();
|
||||
let power_levels = services().rooms.state_accessor.power_levels(&pdu.room_id)?;
|
||||
|
||||
for action in self.get_actions(
|
||||
user,
|
||||
&ruleset,
|
||||
&power_levels,
|
||||
power_levels.into(),
|
||||
&pdu.to_sync_room_event(),
|
||||
&pdu.room_id,
|
||||
)? {
|
||||
|
@ -188,16 +179,10 @@ impl Service {
|
|||
&self,
|
||||
user: &UserId,
|
||||
ruleset: &'a Ruleset,
|
||||
power_levels: &RoomPowerLevelsEventContent,
|
||||
power_levels: PushConditionPowerLevelsCtx,
|
||||
pdu: &Raw<AnySyncTimelineEvent>,
|
||||
room_id: &RoomId,
|
||||
) -> 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 {
|
||||
room_id: room_id.to_owned(),
|
||||
member_count: 10_u32.into(), // TODO: get member count efficiently
|
||||
|
|
|
@ -2,7 +2,6 @@ mod data;
|
|||
|
||||
pub use data::Data;
|
||||
use rand::seq::SliceRandom;
|
||||
use tracing::error;
|
||||
|
||||
use crate::{services, Error, Result};
|
||||
use ruma::{
|
||||
|
@ -11,10 +10,7 @@ use ruma::{
|
|||
client::{alias::get_alias, error::ErrorKind},
|
||||
federation,
|
||||
},
|
||||
events::{
|
||||
room::power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||
StateEventType,
|
||||
},
|
||||
events::StateEventType,
|
||||
OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId, UserId,
|
||||
};
|
||||
|
||||
|
@ -55,27 +51,14 @@ impl Service {
|
|||
{
|
||||
Ok(true)
|
||||
// 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 {
|
||||
error!("Room {} has no m.room.create event (VERY BAD)!", room_id);
|
||||
Err(Error::bad_database("Room has no m.room.create event"))
|
||||
services()
|
||||
.rooms
|
||||
.state_accessor
|
||||
.power_levels(&room_id)
|
||||
.map(|power_levels| {
|
||||
power_levels.user_can_send_state(user_id, StateEventType::RoomCanonicalAlias)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ use ruma::{
|
|||
StateEventType,
|
||||
},
|
||||
room::{JoinRuleSummary, RoomMembership},
|
||||
state_res::Event,
|
||||
state_res::{events::RoomCreateEvent, Event},
|
||||
EventId, JsOption, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
|
@ -361,43 +361,19 @@ impl Service {
|
|||
room_id: &RoomId,
|
||||
federation: bool,
|
||||
) -> Result<bool> {
|
||||
self.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
|
||||
.map(|e| {
|
||||
serde_json::from_str(e.content.get())
|
||||
.map(|c: RoomPowerLevelsEventContent| c.into())
|
||||
.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 {
|
||||
pdu.sender().server_name() == sender.server_name()
|
||||
} else {
|
||||
pdu.sender == sender
|
||||
}
|
||||
} else {
|
||||
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
|
||||
self.power_levels(room_id).map(|power_levels| {
|
||||
power_levels.user_can_redact_event_of_other(sender)
|
||||
|| power_levels.user_can_redact_own_event(sender)
|
||||
&& if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) {
|
||||
if federation {
|
||||
pdu.sender().server_name() == sender.server_name()
|
||||
} else {
|
||||
false
|
||||
})
|
||||
} else {
|
||||
Err(Error::bad_database(
|
||||
"No m.room.power_levels or m.room.create events in database for room",
|
||||
))
|
||||
}
|
||||
})
|
||||
pdu.sender == sender
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if guests are able to join a given room
|
||||
|
@ -485,4 +461,42 @@ impl Service {
|
|||
}
|
||||
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()])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,11 @@ use ruma::{
|
|||
push_rules::PushRulesEvent,
|
||||
room::{
|
||||
canonical_alias::RoomCanonicalAliasEventContent, create::RoomCreateEventContent,
|
||||
encrypted::Relation, member::MembershipState,
|
||||
power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent,
|
||||
encrypted::Relation, member::MembershipState, redaction::RoomRedactionEventContent,
|
||||
},
|
||||
GlobalAccountDataEventType, StateEventType, TimelineEventType,
|
||||
},
|
||||
push::{Action, Ruleset, Tweak},
|
||||
push::{Action, PushConditionPowerLevelsCtx, Ruleset, Tweak},
|
||||
state_res::{self, Event},
|
||||
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch,
|
||||
OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId,
|
||||
|
@ -290,96 +289,99 @@ impl Service {
|
|||
drop(insert_lock);
|
||||
|
||||
// See if the event matches any known pushers
|
||||
let power_levels: RoomPowerLevelsEventContent = services()
|
||||
.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();
|
||||
//
|
||||
// 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
|
||||
.state_accessor
|
||||
.power_levels(&pdu.room_id)
|
||||
.map(PushConditionPowerLevelsCtx::from)
|
||||
{
|
||||
let sync_pdu = pdu.to_sync_room_event();
|
||||
|
||||
let sync_pdu = pdu.to_sync_room_event();
|
||||
let mut notifies = Vec::new();
|
||||
let mut highlights = Vec::new();
|
||||
|
||||
let mut notifies = Vec::new();
|
||||
let mut highlights = Vec::new();
|
||||
let mut push_target = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_our_real_users(&pdu.room_id)?;
|
||||
|
||||
let mut push_target = services()
|
||||
.rooms
|
||||
.state_cache
|
||||
.get_our_real_users(&pdu.room_id)?;
|
||||
if pdu.kind == TimelineEventType::RoomMember {
|
||||
if let Some(state_key) = &pdu.state_key {
|
||||
let target_user_id = UserId::parse(state_key.clone())
|
||||
.expect("This state_key was previously validated");
|
||||
|
||||
if pdu.kind == TimelineEventType::RoomMember {
|
||||
if let Some(state_key) = &pdu.state_key {
|
||||
let target_user_id = UserId::parse(state_key.clone())
|
||||
.expect("This state_key was previously validated");
|
||||
|
||||
if !push_target.contains(&target_user_id) {
|
||||
let mut target = push_target.as_ref().clone();
|
||||
target.insert(target_user_id);
|
||||
push_target = Arc::new(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for user in push_target.iter() {
|
||||
// Don't notify the user of their own events
|
||||
if user == &pdu.sender {
|
||||
continue;
|
||||
}
|
||||
|
||||
let rules_for_user = services()
|
||||
.account_data
|
||||
.get(
|
||||
None,
|
||||
user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
)?
|
||||
.map(|event| {
|
||||
serde_json::from_str::<PushRulesEvent>(event.get())
|
||||
.map_err(|_| Error::bad_database("Invalid push rules event in db."))
|
||||
})
|
||||
.transpose()?
|
||||
.map(|ev: PushRulesEvent| ev.content.global)
|
||||
.unwrap_or_else(|| Ruleset::server_default(user));
|
||||
|
||||
let mut highlight = false;
|
||||
let mut notify = false;
|
||||
|
||||
for action in services().pusher.get_actions(
|
||||
user,
|
||||
&rules_for_user,
|
||||
&power_levels,
|
||||
&sync_pdu,
|
||||
&pdu.room_id,
|
||||
)? {
|
||||
match action {
|
||||
Action::Notify => notify = true,
|
||||
Action::SetTweak(Tweak::Highlight(true)) => {
|
||||
highlight = true;
|
||||
if !push_target.contains(&target_user_id) {
|
||||
let mut target = push_target.as_ref().clone();
|
||||
target.insert(target_user_id);
|
||||
push_target = Arc::new(target);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if notify {
|
||||
notifies.push(user.clone());
|
||||
}
|
||||
for user in push_target.iter() {
|
||||
// Don't notify the user of their own events
|
||||
if user == &pdu.sender {
|
||||
continue;
|
||||
}
|
||||
|
||||
if highlight {
|
||||
highlights.push(user.clone());
|
||||
}
|
||||
let rules_for_user = services()
|
||||
.account_data
|
||||
.get(
|
||||
None,
|
||||
user,
|
||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||
)?
|
||||
.map(|event| {
|
||||
serde_json::from_str::<PushRulesEvent>(event.get())
|
||||
.map_err(|_| Error::bad_database("Invalid push rules event in db."))
|
||||
})
|
||||
.transpose()?
|
||||
.map(|ev: PushRulesEvent| ev.content.global)
|
||||
.unwrap_or_else(|| Ruleset::server_default(user));
|
||||
|
||||
for push_key in services().pusher.get_pushkeys(user) {
|
||||
services().sending.send_push_pdu(&pdu_id, user, push_key?)?;
|
||||
let mut highlight = false;
|
||||
let mut notify = false;
|
||||
|
||||
for action in services().pusher.get_actions(
|
||||
user,
|
||||
&rules_for_user,
|
||||
power_levels.clone(),
|
||||
&sync_pdu,
|
||||
&pdu.room_id,
|
||||
)? {
|
||||
match action {
|
||||
Action::Notify => notify = true,
|
||||
Action::SetTweak(Tweak::Highlight(true)) => {
|
||||
highlight = true;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
if notify {
|
||||
notifies.push(user.clone());
|
||||
}
|
||||
|
||||
if highlight {
|
||||
highlights.push(user.clone());
|
||||
}
|
||||
|
||||
for push_key in services().pusher.get_pushkeys(user) {
|
||||
services().sending.send_push_pdu(&pdu_id, user, push_key?)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.db
|
||||
.increment_notification_counts(&pdu.room_id, notifies, highlights)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.db
|
||||
.increment_notification_counts(&pdu.room_id, notifies, highlights)?;
|
||||
|
||||
match pdu.kind {
|
||||
TimelineEventType::RoomRedaction => {
|
||||
let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?;
|
||||
|
@ -1167,16 +1169,8 @@ impl Service {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let power_levels: RoomPowerLevelsEventContent = services()
|
||||
.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 power_levels = services().rooms.state_accessor.power_levels(room_id)?;
|
||||
|
||||
let mut admin_servers = power_levels
|
||||
.users
|
||||
.iter()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue