mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
refactor: Ruma upstraming, bake a little more
This commit is contained in:
Generated
+7
@@ -999,6 +999,7 @@ dependencies = [
|
|||||||
name = "conduwuit_admin"
|
name = "conduwuit_admin"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assign",
|
||||||
"clap",
|
"clap",
|
||||||
"conduwuit_api",
|
"conduwuit_api",
|
||||||
"conduwuit_core",
|
"conduwuit_core",
|
||||||
@@ -1022,6 +1023,7 @@ dependencies = [
|
|||||||
name = "conduwuit_api"
|
name = "conduwuit_api"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assign",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"axum-client-ip",
|
"axum-client-ip",
|
||||||
@@ -1068,6 +1070,7 @@ version = "0.5.8"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"arrayvec",
|
"arrayvec",
|
||||||
|
"assign",
|
||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -1161,6 +1164,7 @@ dependencies = [
|
|||||||
name = "conduwuit_router"
|
name = "conduwuit_router"
|
||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assign",
|
||||||
"axum",
|
"axum",
|
||||||
"axum-client-ip",
|
"axum-client-ip",
|
||||||
"axum-server",
|
"axum-server",
|
||||||
@@ -1198,6 +1202,7 @@ name = "conduwuit_service"
|
|||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
|
"assign",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"blurhash",
|
"blurhash",
|
||||||
@@ -1247,6 +1252,7 @@ name = "conduwuit_web"
|
|||||||
version = "0.5.8"
|
version = "0.5.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"askama",
|
"askama",
|
||||||
|
"assign",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"axum-extra",
|
"axum-extra",
|
||||||
@@ -4846,6 +4852,7 @@ dependencies = [
|
|||||||
name = "ruminuwuity"
|
name = "ruminuwuity"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"assign",
|
||||||
"ruma",
|
"ruma",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -342,6 +342,9 @@ version = "0.1.88"
|
|||||||
[workspace.dependencies.lru-cache]
|
[workspace.dependencies.lru-cache]
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
|
||||||
|
[workspace.dependencies.assign]
|
||||||
|
version = "1.1.1"
|
||||||
|
|
||||||
# Used for matrix spec type definitions and helpers
|
# Used for matrix spec type definitions and helpers
|
||||||
[workspace.dependencies.ruma]
|
[workspace.dependencies.ruma]
|
||||||
# version = "0.14.1"
|
# version = "0.14.1"
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ ctor.workspace = true
|
|||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
lettre.workspace = true
|
lettre.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
assign.workspace = true
|
||||||
ruma.workspace = true
|
ruma.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde-saphyr.workspace = true
|
serde-saphyr.workspace = true
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ lettre.workspace = true
|
|||||||
log.workspace = true
|
log.workspace = true
|
||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
|
assign.workspace = true
|
||||||
ruma.workspace = true
|
ruma.workspace = true
|
||||||
ruminuwuity.workspace = true
|
ruminuwuity.workspace = true
|
||||||
serde_html_form.workspace = true
|
serde_html_form.workspace = true
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{Err, Result, info, utils::ReadyExt, warn};
|
use conduwuit::{Err, Result, info, utils::ReadyExt, warn};
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use ruma::{
|
use ruma::{OwnedRoomAliasId, events::room::message::RoomMessageEventContent};
|
||||||
OwnedRoomAliasId,
|
|
||||||
events::room::message::RoomMessageEventContent,
|
|
||||||
};
|
|
||||||
use ruminuwuity::admin::continuwuity::rooms;
|
use ruminuwuity::admin::continuwuity::rooms;
|
||||||
|
|
||||||
use crate::{Ruma, client::leave_room};
|
use crate::{Ruma, client::leave_room};
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ pub(crate) async fn update_device_route(
|
|||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Creating new device for {sender_user} from appservice {} as device ID does not exist",
|
"Creating new device for {sender_user} from appservice {} as device ID does not \
|
||||||
|
exist",
|
||||||
appservice.registration.id
|
appservice.registration.id
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -121,9 +122,7 @@ pub(crate) async fn delete_device_route(
|
|||||||
let appservice = body.appservice_info.as_ref();
|
let appservice = body.appservice_info.as_ref();
|
||||||
|
|
||||||
if appservice.is_some() {
|
if appservice.is_some() {
|
||||||
debug!(
|
debug!("Skipping UIAA for {sender_user} as this is from an appservice");
|
||||||
"Skipping UIAA for {sender_user} as this is from an appservice"
|
|
||||||
);
|
|
||||||
services
|
services
|
||||||
.users
|
.users
|
||||||
.remove_device(sender_user, &body.device_id)
|
.remove_device(sender_user, &body.device_id)
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ use futures::{
|
|||||||
future::{join, join4, join5},
|
future::{join, join4, join5},
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, RoomId, ServerName, UInt, UserId, api::{
|
OwnedRoomId, RoomId, ServerName, UInt, UserId,
|
||||||
|
api::{
|
||||||
client::{
|
client::{
|
||||||
directory::{
|
directory::{
|
||||||
get_public_rooms, get_public_rooms_filtered, get_room_visibility,
|
get_public_rooms, get_public_rooms_filtered, get_room_visibility,
|
||||||
@@ -26,14 +27,18 @@ use ruma::{
|
|||||||
room,
|
room,
|
||||||
},
|
},
|
||||||
federation,
|
federation,
|
||||||
}, directory::{Filter, PublicRoomsChunk, RoomNetwork, RoomTypeFilter}, events::{
|
},
|
||||||
|
directory::{Filter, PublicRoomsChunk, RoomNetwork, RoomTypeFilter},
|
||||||
|
events::{
|
||||||
StateEventType,
|
StateEventType,
|
||||||
room::{
|
room::{
|
||||||
create::RoomCreateEventContent,
|
create::RoomCreateEventContent,
|
||||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||||
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||||
},
|
},
|
||||||
}, room::JoinRuleKind, uint
|
},
|
||||||
|
room::JoinRuleKind,
|
||||||
|
uint,
|
||||||
};
|
};
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,10 @@ use conduwuit::{
|
|||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
RoomId, UserId,
|
RoomId, UserId,
|
||||||
api::{client::membership::invite_user, federation::membership::{RawStrippedState, create_invite}},
|
api::{
|
||||||
|
client::membership::invite_user,
|
||||||
|
federation::membership::{RawStrippedState, create_invite},
|
||||||
|
},
|
||||||
events::room::member::{MembershipState, RoomMemberEventContent},
|
events::room::member::{MembershipState, RoomMemberEventContent},
|
||||||
};
|
};
|
||||||
use ruminuwuity::invite_permission_config::FilterLevel;
|
use ruminuwuity::invite_permission_config::FilterLevel;
|
||||||
|
|||||||
@@ -208,10 +208,7 @@ pub(crate) async fn join_room_by_id_or_alias_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let addl_via_servers = services
|
let addl_via_servers = services.rooms.state_cache.servers_invite_via(&room_id);
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.servers_invite_via(&room_id);
|
|
||||||
|
|
||||||
let addl_state_servers = services
|
let addl_state_servers = services
|
||||||
.rooms
|
.rooms
|
||||||
@@ -432,8 +429,7 @@ async fn join_room_by_id_helper_remote(
|
|||||||
|
|
||||||
join_event_stub.insert(
|
join_event_stub.insert(
|
||||||
"content".to_owned(),
|
"content".to_owned(),
|
||||||
to_canonical_value(join_content)
|
to_canonical_value(join_content).expect("event is valid, we just created it"),
|
||||||
.expect("event is valid, we just created it"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// We keep the "event_id" in the pdu only in v1 or
|
// We keep the "event_id" in the pdu only in v1 or
|
||||||
@@ -462,10 +458,14 @@ async fn join_room_by_id_helper_remote(
|
|||||||
let mut join_event = join_event_stub;
|
let mut join_event = join_event_stub;
|
||||||
|
|
||||||
info!("Asking {remote_server} for send_join in room {room_id}");
|
info!("Asking {remote_server} for send_join in room {room_id}");
|
||||||
let send_join_request = federation::membership::create_join_event::v2::Request::new(room_id.to_owned(), event_id.clone(), services
|
let send_join_request = federation::membership::create_join_event::v2::Request::new(
|
||||||
|
room_id.to_owned(),
|
||||||
|
event_id.clone(),
|
||||||
|
services
|
||||||
.sending
|
.sending
|
||||||
.convert_to_outgoing_federation_event(join_event.clone())
|
.convert_to_outgoing_federation_event(join_event.clone())
|
||||||
.await);
|
.await,
|
||||||
|
);
|
||||||
|
|
||||||
let send_join_response = match services
|
let send_join_response = match services
|
||||||
.sending
|
.sending
|
||||||
@@ -816,15 +816,15 @@ async fn make_join_request(
|
|||||||
servers.len()
|
servers.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut request = federation::membership::prepare_join_event::v1::Request::new(room_id.to_owned(), sender_user.to_owned());
|
let mut request = federation::membership::prepare_join_event::v1::Request::new(
|
||||||
|
room_id.to_owned(),
|
||||||
|
sender_user.to_owned(),
|
||||||
|
);
|
||||||
request.ver = services.server.supported_room_versions().collect();
|
request.ver = services.server.supported_room_versions().collect();
|
||||||
|
|
||||||
let make_join_response = services
|
let make_join_response = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(remote_server, request)
|
||||||
remote_server,
|
|
||||||
request
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
trace!("make_join response: {:?}", make_join_response);
|
trace!("make_join response: {:?}", make_join_response);
|
||||||
|
|||||||
@@ -15,16 +15,20 @@ use conduwuit::{
|
|||||||
};
|
};
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, RoomVersionId, UserId, api::{
|
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, OwnedServerName,
|
||||||
|
OwnedUserId, RoomId, RoomVersionId, UserId,
|
||||||
|
api::{
|
||||||
client::knock::knock_room,
|
client::knock::knock_room,
|
||||||
federation::{self},
|
federation::{self},
|
||||||
}, canonical_json::to_canonical_value, events::{
|
},
|
||||||
|
canonical_json::to_canonical_value,
|
||||||
|
events::{
|
||||||
StateEventType,
|
StateEventType,
|
||||||
room::{
|
room::{
|
||||||
join_rules::{AllowRule, JoinRule},
|
join_rules::{AllowRule, JoinRule},
|
||||||
member::{MembershipState, RoomMemberEventContent},
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
use service::{
|
use service::{
|
||||||
Services,
|
Services,
|
||||||
@@ -108,10 +112,7 @@ pub(crate) async fn knock_room_route(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let addl_via_servers = services
|
let addl_via_servers = services.rooms.state_cache.servers_invite_via(&room_id);
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.servers_invite_via(&room_id);
|
|
||||||
|
|
||||||
let addl_state_servers = services
|
let addl_state_servers = services
|
||||||
.rooms
|
.rooms
|
||||||
@@ -416,8 +417,7 @@ async fn knock_room_helper_local(
|
|||||||
);
|
);
|
||||||
knock_event_stub.insert(
|
knock_event_stub.insert(
|
||||||
"content".to_owned(),
|
"content".to_owned(),
|
||||||
to_canonical_value(content)
|
to_canonical_value(content).expect("event is valid, we just created it"),
|
||||||
.expect("event is valid, we just created it"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||||
@@ -540,8 +540,7 @@ async fn knock_room_helper_remote(
|
|||||||
|
|
||||||
knock_event_stub.insert(
|
knock_event_stub.insert(
|
||||||
"content".to_owned(),
|
"content".to_owned(),
|
||||||
to_canonical_value(knock_content)
|
to_canonical_value(knock_content).expect("event is valid, we just created it"),
|
||||||
.expect("event is valid, we just created it"),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
// In order to create a compatible ref hash (EventID) the `hashes` field needs
|
||||||
@@ -567,7 +566,7 @@ async fn knock_room_helper_remote(
|
|||||||
services
|
services
|
||||||
.sending
|
.sending
|
||||||
.convert_to_outgoing_federation_event(knock_event.clone())
|
.convert_to_outgoing_federation_event(knock_event.clone())
|
||||||
.await
|
.await,
|
||||||
);
|
);
|
||||||
|
|
||||||
let send_knock_response = services
|
let send_knock_response = services
|
||||||
@@ -594,13 +593,10 @@ async fn knock_room_helper_remote(
|
|||||||
.map(|event| {
|
.map(|event| {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let raw_value = match event {
|
let raw_value = match event {
|
||||||
federation::membership::RawStrippedState::Stripped(raw_state) => {
|
| federation::membership::RawStrippedState::Stripped(raw_state) =>
|
||||||
&raw_state.clone().into_json()
|
&raw_state.clone().into_json(),
|
||||||
},
|
| federation::membership::RawStrippedState::Pdu(raw_value) => raw_value,
|
||||||
federation::membership::RawStrippedState::Pdu(raw_value) => {
|
| _ => panic!("unknown raw stripped state type"),
|
||||||
raw_value
|
|
||||||
},
|
|
||||||
_ => panic!("unknown raw stripped state type"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
serde_json::from_str::<CanonicalJsonObject>(raw_value.get())
|
serde_json::from_str::<CanonicalJsonObject>(raw_value.get())
|
||||||
@@ -722,15 +718,15 @@ async fn make_knock_request(
|
|||||||
|
|
||||||
info!("Asking {remote_server} for make_knock ({make_knock_counter})");
|
info!("Asking {remote_server} for make_knock ({make_knock_counter})");
|
||||||
|
|
||||||
let mut request = federation::membership::prepare_knock_event::v1::Request::new(room_id.to_owned(), sender_user.to_owned());
|
let mut request = federation::membership::prepare_knock_event::v1::Request::new(
|
||||||
|
room_id.to_owned(),
|
||||||
|
sender_user.to_owned(),
|
||||||
|
);
|
||||||
request.ver = services.server.supported_room_versions().collect();
|
request.ver = services.server.supported_room_versions().collect();
|
||||||
|
|
||||||
let make_knock_response = services
|
let make_knock_response = services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(remote_server, request)
|
||||||
remote_server,
|
|
||||||
request,
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
trace!("make_knock response: {make_knock_response:?}");
|
trace!("make_knock response: {make_knock_response:?}");
|
||||||
|
|||||||
@@ -42,10 +42,7 @@ pub(crate) async fn leave_room_route(
|
|||||||
// Make a user leave all their joined rooms, rescinds knocks, forgets all rooms,
|
// Make a user leave all their joined rooms, rescinds knocks, forgets all rooms,
|
||||||
// and ignores errors
|
// and ignores errors
|
||||||
pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
|
pub async fn leave_all_rooms(services: &Services, user_id: &UserId) {
|
||||||
let rooms_joined = services
|
let rooms_joined = services.rooms.state_cache.rooms_joined(user_id);
|
||||||
.rooms
|
|
||||||
.state_cache
|
|
||||||
.rooms_joined(user_id);
|
|
||||||
|
|
||||||
let rooms_invited = services
|
let rooms_invited = services
|
||||||
.rooms
|
.rooms
|
||||||
@@ -286,7 +283,10 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
|||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
remote_server.as_ref(),
|
remote_server.as_ref(),
|
||||||
federation::membership::prepare_leave_event::v1::Request::new(room_id.to_owned(), user_id.to_owned())
|
federation::membership::prepare_leave_event::v1::Request::new(
|
||||||
|
room_id.to_owned(),
|
||||||
|
user_id.to_owned(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@@ -387,10 +387,14 @@ pub async fn remote_leave_room<S: ::std::hash::BuildHasher>(
|
|||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
&remote_server,
|
&remote_server,
|
||||||
federation::membership::create_leave_event::v2::Request::new(room_id.to_owned(), event_id.clone(), services
|
federation::membership::create_leave_event::v2::Request::new(
|
||||||
|
room_id.to_owned(),
|
||||||
|
event_id.clone(),
|
||||||
|
services
|
||||||
.sending
|
.sending
|
||||||
.convert_to_outgoing_federation_event(leave_event.clone())
|
.convert_to_outgoing_federation_event(leave_event.clone())
|
||||||
.await),
|
.await,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|||||||
@@ -44,17 +44,17 @@ pub(crate) async fn get_member_events_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let chunk = services
|
let chunk = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_full(&body.room_id)
|
.room_state_full(&body.room_id)
|
||||||
.ready_filter_map(Result::ok)
|
.ready_filter_map(Result::ok)
|
||||||
.ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember)
|
.ready_filter(|((ty, _), _)| *ty == StateEventType::RoomMember)
|
||||||
.map(at!(1))
|
.map(at!(1))
|
||||||
.ready_filter_map(|pdu| membership_filter(pdu, membership, not_membership))
|
.ready_filter_map(|pdu| membership_filter(pdu, membership, not_membership))
|
||||||
.map(Event::into_format)
|
.map(Event::into_format)
|
||||||
.collect()
|
.collect()
|
||||||
.boxed()
|
.boxed()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(get_member_events::v3::Response::new(chunk))
|
Ok(get_member_events::v3::Response::new(chunk))
|
||||||
}
|
}
|
||||||
@@ -79,23 +79,23 @@ pub(crate) async fn joined_members_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let joined = services
|
let joined = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.room_members(&body.room_id)
|
.room_members(&body.room_id)
|
||||||
.broad_then(|user_id| async move {
|
.broad_then(|user_id| async move {
|
||||||
let mut member = RoomMember::new();
|
let mut member = RoomMember::new();
|
||||||
let (display_name, avatar_url) = join(
|
let (display_name, avatar_url) = join(
|
||||||
services.users.displayname(&user_id).ok(),
|
services.users.displayname(&user_id).ok(),
|
||||||
services.users.avatar_url(&user_id).ok(),
|
services.users.avatar_url(&user_id).ok(),
|
||||||
)
|
)
|
||||||
.await;
|
|
||||||
member.display_name = display_name;
|
|
||||||
member.avatar_url = avatar_url;
|
|
||||||
|
|
||||||
(user_id, member)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
.await;
|
.await;
|
||||||
|
member.display_name = display_name;
|
||||||
|
member.avatar_url = avatar_url;
|
||||||
|
|
||||||
|
(user_id, member)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
|
||||||
Ok(joined_members::v3::Response::new(joined))
|
Ok(joined_members::v3::Response::new(joined))
|
||||||
}
|
}
|
||||||
@@ -108,12 +108,14 @@ fn membership_filter<Pdu: Event>(
|
|||||||
let evt_membership = pdu.get_content::<RoomMemberEventContent>().ok()?.membership;
|
let evt_membership = pdu.get_content::<RoomMemberEventContent>().ok()?.membership;
|
||||||
|
|
||||||
if let Some(membership_state_filter) = membership_state_filter
|
if let Some(membership_state_filter) = membership_state_filter
|
||||||
&& *membership_state_filter != evt_membership {
|
&& *membership_state_filter != evt_membership
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(not_membership_state_filter) = not_membership_state_filter
|
if let Some(not_membership_state_filter) = not_membership_state_filter
|
||||||
&& *not_membership_state_filter == evt_membership {
|
&& *not_membership_state_filter == evt_membership
|
||||||
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -48,11 +48,11 @@ pub(crate) async fn joined_rooms_route(
|
|||||||
body: Ruma<joined_rooms::v3::Request>,
|
body: Ruma<joined_rooms::v3::Request>,
|
||||||
) -> Result<joined_rooms::v3::Response> {
|
) -> Result<joined_rooms::v3::Response> {
|
||||||
let joined_rooms = services
|
let joined_rooms = services
|
||||||
.rooms
|
.rooms
|
||||||
.state_cache
|
.state_cache
|
||||||
.rooms_joined(body.sender_user())
|
.rooms_joined(body.sender_user())
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(joined_rooms::v3::Response::new(joined_rooms))
|
Ok(joined_rooms::v3::Response::new(joined_rooms))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ pub(crate) async fn get_room_aliases_route(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let aliases = services
|
let aliases = services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
.local_aliases_for_room(&body.room_id)
|
.local_aliases_for_room(&body.room_id)
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(aliases::v3::Response::new(aliases))
|
Ok(aliases::v3::Response::new(aliases))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ use conduwuit::{
|
|||||||
use conduwuit_service::{Services, appservice::RegistrationInfo};
|
use conduwuit_service::{Services, appservice::RegistrationInfo};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId, RoomVersionId, api::client::room::{self, create_room}, events::{
|
CanonicalJsonObject, Int, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, RoomId, RoomVersionId,
|
||||||
|
api::client::room::{self, create_room},
|
||||||
|
events::{
|
||||||
TimelineEventType,
|
TimelineEventType,
|
||||||
room::{
|
room::{
|
||||||
canonical_alias::RoomCanonicalAliasEventContent,
|
canonical_alias::RoomCanonicalAliasEventContent,
|
||||||
@@ -22,7 +24,10 @@ use ruma::{
|
|||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent,
|
||||||
topic::RoomTopicEventContent,
|
topic::RoomTopicEventContent,
|
||||||
},
|
},
|
||||||
}, int, room_version_rules::RoomIdFormatVersion, serde::{JsonObject, Raw}
|
},
|
||||||
|
int,
|
||||||
|
room_version_rules::RoomIdFormatVersion,
|
||||||
|
serde::{JsonObject, Raw},
|
||||||
};
|
};
|
||||||
use ruminuwuity::invite_permission_config::FilterLevel;
|
use ruminuwuity::invite_permission_config::FilterLevel;
|
||||||
use serde_json::{json, value::to_raw_value};
|
use serde_json::{json, value::to_raw_value};
|
||||||
@@ -80,8 +85,8 @@ pub(crate) async fn create_room_route(
|
|||||||
let room_version_rules = room_version.rules().unwrap();
|
let room_version_rules = room_version.rules().unwrap();
|
||||||
|
|
||||||
let room_id: Option<OwnedRoomId> = match room_version_rules.room_id_format {
|
let room_id: Option<OwnedRoomId> = match room_version_rules.room_id_format {
|
||||||
RoomIdFormatVersion::V1 => Some(RoomId::new_v1(services.globals.server_name())),
|
| RoomIdFormatVersion::V1 => Some(RoomId::new_v1(services.globals.server_name())),
|
||||||
_ => None,
|
| _ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// check if room ID doesn't already exist instead of erroring on auth check
|
// check if room ID doesn't already exist instead of erroring on auth check
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ macro_rules! ruma_handler {
|
|||||||
{
|
{
|
||||||
fn add_routes(&'static self, router: Router<State>) -> Router<State> {
|
fn add_routes(&'static self, router: Router<State>) -> Router<State> {
|
||||||
use ruma::api::path_builder::PathBuilder;
|
use ruma::api::path_builder::PathBuilder;
|
||||||
|
|
||||||
Req::PATH_BUILDER
|
Req::PATH_BUILDER
|
||||||
.all_paths()
|
.all_paths()
|
||||||
.fold(router, |router, path| self.add_route(router, path))
|
.fold(router, |router, path| self.add_route(router, path))
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ rand_core = { version = "0.6.4", features = ["getrandom"] }
|
|||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
|
assign.workspace = true
|
||||||
ruma.workspace = true
|
ruma.workspace = true
|
||||||
sanitize-filename.workspace = true
|
sanitize-filename.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ macro_rules! err {
|
|||||||
(Request(Forbidden($level:ident!($($args:tt)+)))) => {{
|
(Request(Forbidden($level:ident!($($args:tt)+)))) => {{
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
$crate::error::Error::Request(
|
$crate::error::Error::Request(
|
||||||
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
$crate::ruma::api::error::ErrorKind::Forbidden,
|
||||||
$crate::err_log!(buf, $level, $($args)+),
|
$crate::err_log!(buf, $level, $($args)+),
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
$crate::http::StatusCode::BAD_REQUEST
|
||||||
)
|
)
|
||||||
@@ -54,7 +54,7 @@ macro_rules! err {
|
|||||||
|
|
||||||
(Request(Forbidden($($args:tt)+))) => {
|
(Request(Forbidden($($args:tt)+))) => {
|
||||||
$crate::error::Error::Request(
|
$crate::error::Error::Request(
|
||||||
$crate::ruma::api::client::error::ErrorKind::forbidden(),
|
$crate::ruma::api::error::ErrorKind::Forbidden,
|
||||||
$crate::format_maybe!($($args)+),
|
$crate::format_maybe!($($args)+),
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
$crate::http::StatusCode::BAD_REQUEST
|
||||||
)
|
)
|
||||||
@@ -63,7 +63,7 @@ macro_rules! err {
|
|||||||
(Request($variant:ident($level:ident!($($args:tt)+)))) => {{
|
(Request($variant:ident($level:ident!($($args:tt)+)))) => {{
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
$crate::error::Error::Request(
|
$crate::error::Error::Request(
|
||||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
$crate::ruma::api::error::ErrorKind::$variant,
|
||||||
$crate::err_log!(buf, $level, $($args)+),
|
$crate::err_log!(buf, $level, $($args)+),
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
$crate::http::StatusCode::BAD_REQUEST
|
||||||
)
|
)
|
||||||
@@ -71,7 +71,7 @@ macro_rules! err {
|
|||||||
|
|
||||||
(Request($variant:ident($($args:tt)+))) => {
|
(Request($variant:ident($($args:tt)+))) => {
|
||||||
$crate::error::Error::Request(
|
$crate::error::Error::Request(
|
||||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
$crate::ruma::api::error::ErrorKind::$variant,
|
||||||
$crate::format_maybe!($($args)+),
|
$crate::format_maybe!($($args)+),
|
||||||
$crate::http::StatusCode::BAD_REQUEST
|
$crate::http::StatusCode::BAD_REQUEST
|
||||||
)
|
)
|
||||||
@@ -79,7 +79,7 @@ macro_rules! err {
|
|||||||
|
|
||||||
(Request($variant:ident($($args:tt)+), $status_code:ident)) => {
|
(Request($variant:ident($($args:tt)+), $status_code:ident)) => {
|
||||||
$crate::error::Error::Request(
|
$crate::error::Error::Request(
|
||||||
$crate::ruma::api::client::error::ErrorKind::$variant,
|
$crate::ruma::api::error::ErrorKind::$variant,
|
||||||
$crate::format_maybe!($($args)+),
|
$crate::format_maybe!($($args)+),
|
||||||
$crate::http::StatusCode::$status_code,
|
$crate::http::StatusCode::$status_code,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ pub enum Error {
|
|||||||
#[error("Arithmetic operation failed: {0}")]
|
#[error("Arithmetic operation failed: {0}")]
|
||||||
Arithmetic(Cow<'static, str>),
|
Arithmetic(Cow<'static, str>),
|
||||||
#[error("{0:?}: {1}")]
|
#[error("{0:?}: {1}")]
|
||||||
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
|
BadRequest(ruma::api::error::ErrorKind, &'static str), //TODO: remove
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
BadServerResponse(Cow<'static, str>),
|
BadServerResponse(Cow<'static, str>),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
@@ -103,7 +103,7 @@ pub enum Error {
|
|||||||
#[error("Feature '{0}' is not available on this server.")]
|
#[error("Feature '{0}' is not available on this server.")]
|
||||||
FeatureDisabled(Cow<'static, str>),
|
FeatureDisabled(Cow<'static, str>),
|
||||||
#[error("Remote server {0} responded with: {1}")]
|
#[error("Remote server {0} responded with: {1}")]
|
||||||
Federation(ruma::OwnedServerName, ruma::api::client::error::Error),
|
Federation(ruma::OwnedServerName, ruma::api::error::Error),
|
||||||
#[error("{0} in {1}")]
|
#[error("{0} in {1}")]
|
||||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
@@ -117,11 +117,13 @@ pub enum Error {
|
|||||||
#[error("from {0}: {1}")]
|
#[error("from {0}: {1}")]
|
||||||
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
|
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
|
||||||
#[error("{0:?}: {1}")]
|
#[error("{0:?}: {1}")]
|
||||||
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>, http::StatusCode),
|
Request(ruma::api::error::ErrorKind, Cow<'static, str>, http::StatusCode),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Ruma(#[from] ruma::api::client::error::Error),
|
Ruma(#[from] ruma::api::error::Error),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Signatures(#[from] ruma::signatures::Error),
|
SignatureJson(#[from] ruma::signatures::JsonError),
|
||||||
|
#[error(transparent)]
|
||||||
|
SignatureVerification(#[from] ruma::signatures::VerificationError),
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
StateRes(#[from] crate::state_res::Error),
|
StateRes(#[from] crate::state_res::Error),
|
||||||
#[error("uiaa")]
|
#[error("uiaa")]
|
||||||
@@ -162,8 +164,8 @@ impl Error {
|
|||||||
|
|
||||||
/// Returns the Matrix error code / error kind
|
/// Returns the Matrix error code / error kind
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn kind(&self) -> ruma::api::client::error::ErrorKind {
|
pub fn kind(&self) -> ruma::api::error::ErrorKind {
|
||||||
use ruma::api::client::error::ErrorKind::{Unknown, Unrecognized};
|
use ruma::api::error::ErrorKind::{Unknown, Unrecognized};
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
| Self::Federation(_, error) | Self::Ruma(error) =>
|
| Self::Federation(_, error) | Self::Ruma(error) =>
|
||||||
|
|||||||
+10
-14
@@ -3,10 +3,8 @@ use http::StatusCode;
|
|||||||
use http_body_util::Full;
|
use http_body_util::Full;
|
||||||
use ruma::api::{
|
use ruma::api::{
|
||||||
OutgoingResponse,
|
OutgoingResponse,
|
||||||
client::{
|
client::uiaa::UiaaResponse,
|
||||||
error::{ErrorBody, ErrorKind, StandardErrorBody},
|
error::{ErrorBody, ErrorKind, StandardErrorBody},
|
||||||
uiaa::UiaaResponse,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Error;
|
use super::Error;
|
||||||
@@ -53,7 +51,7 @@ impl From<Error> for UiaaResponse {
|
|||||||
|
|
||||||
let body = ErrorBody::Standard(StandardErrorBody::new(error.kind(), error.message()));
|
let body = ErrorBody::Standard(StandardErrorBody::new(error.kind(), error.message()));
|
||||||
|
|
||||||
Self::MatrixError(ruma::api::client::error::Error::new(error.status_code(), body))
|
Self::MatrixError(ruma::api::error::Error::new(error.status_code(), body))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +68,7 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
|||||||
|
|
||||||
match kind {
|
match kind {
|
||||||
// 429
|
// 429
|
||||||
| LimitExceeded { .. } => StatusCode::TOO_MANY_REQUESTS,
|
| LimitExceeded(_) => StatusCode::TOO_MANY_REQUESTS,
|
||||||
|
|
||||||
// 413
|
// 413
|
||||||
| TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
| TooLarge => StatusCode::PAYLOAD_TOO_LARGE,
|
||||||
@@ -79,28 +77,26 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
|
|||||||
| Unrecognized => StatusCode::METHOD_NOT_ALLOWED,
|
| Unrecognized => StatusCode::METHOD_NOT_ALLOWED,
|
||||||
|
|
||||||
// 404
|
// 404
|
||||||
| NotFound =>
|
| NotFound => StatusCode::NOT_FOUND,
|
||||||
StatusCode::NOT_FOUND,
|
|
||||||
|
|
||||||
// 403
|
// 403
|
||||||
| GuestAccessForbidden
|
| GuestAccessForbidden
|
||||||
| ThreepidAuthFailed
|
| ThreepidAuthFailed
|
||||||
| UserDeactivated
|
| UserDeactivated
|
||||||
| ThreepidDenied
|
| ThreepidDenied
|
||||||
| WrongRoomKeysVersion { .. }
|
| WrongRoomKeysVersion(_)
|
||||||
| UserSuspended
|
| UserSuspended
|
||||||
| Forbidden { .. } => StatusCode::FORBIDDEN,
|
| Forbidden => StatusCode::FORBIDDEN,
|
||||||
|
|
||||||
// 401
|
// 401
|
||||||
| UnknownToken { .. } | MissingToken | Unauthorized | UserLocked =>
|
| UnknownToken(_) | MissingToken | Unauthorized | UserLocked => StatusCode::UNAUTHORIZED,
|
||||||
StatusCode::UNAUTHORIZED,
|
|
||||||
|
|
||||||
// 400
|
// 400
|
||||||
| _ => StatusCode::BAD_REQUEST,
|
| _ => StatusCode::BAD_REQUEST,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> String {
|
pub(super) fn ruma_error_message(error: &ruma::api::error::Error) -> String {
|
||||||
if let ErrorBody::Standard(StandardErrorBody { message, .. }) = &error.body {
|
if let ErrorBody::Standard(StandardErrorBody { message, .. }) = &error.body {
|
||||||
return message.clone();
|
return message.clone();
|
||||||
}
|
}
|
||||||
@@ -108,7 +104,7 @@ pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> Str
|
|||||||
format!("{error}")
|
format!("{error}")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn ruma_error_kind(e: &ruma::api::client::error::Error) -> &ErrorKind {
|
pub(super) fn ruma_error_kind(e: &ruma::api::error::Error) -> &ErrorKind {
|
||||||
e.error_kind().unwrap_or(&ErrorKind::Unknown)
|
e.error_kind().unwrap_or(&ErrorKind::Unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ pub fn gen_event_id(
|
|||||||
room_version_id: &RoomVersionId,
|
room_version_id: &RoomVersionId,
|
||||||
) -> Result<OwnedEventId> {
|
) -> Result<OwnedEventId> {
|
||||||
let Some(rules) = room_version_id.rules() else {
|
let Some(rules) = room_version_id.rules() else {
|
||||||
return Err!("Cannot generate event ID for unknown room version {room_version_id}")
|
return Err!("Cannot generate event ID for unknown room version {room_version_id}");
|
||||||
};
|
};
|
||||||
let reference_hash = ruma::signatures::reference_hash(value, &rules)?;
|
let reference_hash = ruma::signatures::reference_hash(value, &rules)?;
|
||||||
let event_id: OwnedEventId = format!("${reference_hash}").try_into()?;
|
let event_id: OwnedEventId = format!("${reference_hash}").try_into()?;
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ pub mod versions;
|
|||||||
pub use event::{Event, TypeExt as EventTypeExt};
|
pub use event::{Event, TypeExt as EventTypeExt};
|
||||||
pub use pdu::{Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId, ShortId};
|
pub use pdu::{Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId, ShortId};
|
||||||
pub use state_key::StateKey;
|
pub use state_key::StateKey;
|
||||||
pub use state_res::{StateMap, TypeStateKey};
|
pub use state_res::{StateMap, TypeStateKey};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use crate::{Err, Error, Result, err, implement};
|
|||||||
#[implement(super::Pdu)]
|
#[implement(super::Pdu)]
|
||||||
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
|
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
|
||||||
let Some(rules) = room_version_id.rules() else {
|
let Some(rules) = room_version_id.rules() else {
|
||||||
return Err!("Cannot redact event for unknown room version {room_version_id}")
|
return Err!("Cannot redact event for unknown room version {room_version_id}");
|
||||||
};
|
};
|
||||||
|
|
||||||
self.unsigned = None;
|
self.unsigned = None;
|
||||||
@@ -14,8 +14,7 @@ pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) ->
|
|||||||
let mut content = serde_json::from_str(self.content.get())
|
let mut content = serde_json::from_str(self.content.get())
|
||||||
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
|
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
|
||||||
|
|
||||||
redact_content_in_place(&mut content, &rules.redaction, self.kind.to_string())
|
redact_content_in_place(&mut content, &rules.redaction, self.kind.to_string());
|
||||||
.map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?;
|
|
||||||
|
|
||||||
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
|
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,16 @@ use futures::{
|
|||||||
future::{OptionFuture, join, join3},
|
future::{OptionFuture, join, join3},
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
Int, OwnedUserId, RoomVersionId, UserId, events::room::{
|
Int, OwnedUserId, RoomVersionId, UserId,
|
||||||
|
events::room::{
|
||||||
create::RoomCreateEventContent,
|
create::RoomCreateEventContent,
|
||||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||||
member::{MembershipState, ThirdPartyInvite},
|
member::{MembershipState, ThirdPartyInvite},
|
||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent,
|
||||||
}, int, room_version_rules::{RoomIdFormatVersion, RoomVersionRules}, serde::Raw,
|
},
|
||||||
|
int,
|
||||||
|
room_version_rules::{RoomIdFormatVersion, RoomVersionRules},
|
||||||
|
serde::Raw,
|
||||||
};
|
};
|
||||||
use serde::{
|
use serde::{
|
||||||
Deserialize,
|
Deserialize,
|
||||||
@@ -117,11 +121,11 @@ pub fn auth_types_for_event(
|
|||||||
|
|
||||||
// TODO: restore this once 3pid support isn't broken
|
// TODO: restore this once 3pid support isn't broken
|
||||||
// if membership == MembershipState::Invite {
|
// if membership == MembershipState::Invite {
|
||||||
// if let Some(Ok(t_id)) = content.third_party_invite.map(|t| t.deserialize()) {
|
// if let Some(Ok(t_id)) = content.third_party_invite.map(|t|
|
||||||
// let key =
|
// t.deserialize()) { let key =
|
||||||
// (StateEventType::RoomThirdPartyInvite, t_id.signed.token.into());
|
// (StateEventType::RoomThirdPartyInvite,
|
||||||
// if !auth_types.contains(&key) {
|
// t_id.signed.token.into()); if !auth_types.contains(&
|
||||||
// auth_types.push(key);
|
// key) { auth_types.push(key);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
@@ -214,13 +218,17 @@ where
|
|||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if room_version.room_id_format == RoomIdFormatVersion::V2 && incoming_event.room_id().is_some() {
|
if room_version.room_id_format == RoomIdFormatVersion::V2
|
||||||
|
&& incoming_event.room_id().is_some()
|
||||||
|
{
|
||||||
warn!("room create event incorrectly claims to have a room ID when it should not");
|
warn!("room create event incorrectly claims to have a room ID when it should not");
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !room_version.authorization.use_room_create_sender
|
if !room_version.authorization.use_room_create_sender
|
||||||
&& !room_version.authorization.explicitly_privilege_room_creators
|
&& !room_version
|
||||||
|
.authorization
|
||||||
|
.explicitly_privilege_room_creators
|
||||||
{
|
{
|
||||||
// If content has no creator field, reject
|
// If content has no creator field, reject
|
||||||
if content.creator.is_none() {
|
if content.creator.is_none() {
|
||||||
@@ -343,7 +351,7 @@ where
|
|||||||
// Only in some room versions 6 and below
|
// Only in some room versions 6 and below
|
||||||
if room_version.authorization.special_case_room_aliases {
|
if room_version.authorization.special_case_room_aliases {
|
||||||
// 4. If type is m.room.aliases
|
// 4. If type is m.room.aliases
|
||||||
if *incoming_event.event_type() == TimelineEventType::RoomAliases {
|
if *incoming_event.event_type() == TimelineEventType::from("m.room.aliases") {
|
||||||
debug!("starting m.room.aliases check");
|
debug!("starting m.room.aliases check");
|
||||||
|
|
||||||
// If sender's domain doesn't matches state_key, reject
|
// If sender's domain doesn't matches state_key, reject
|
||||||
@@ -493,7 +501,10 @@ where
|
|||||||
if is_creator { int!(100) } else { int!(0) }
|
if is_creator { int!(100) } else { int!(0) }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if room_version.authorization.explicitly_privilege_room_creators {
|
if room_version
|
||||||
|
.authorization
|
||||||
|
.explicitly_privilege_room_creators
|
||||||
|
{
|
||||||
// If the user sent the create event, or is listed in additional_creators, just
|
// If the user sent the create event, or is listed in additional_creators, just
|
||||||
// give them Int::MAX
|
// give them Int::MAX
|
||||||
if sender == room_create_event.sender()
|
if sender == room_create_event.sender()
|
||||||
@@ -555,7 +566,10 @@ where
|
|||||||
if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
|
if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
|
||||||
debug!("starting m.room.power_levels check");
|
debug!("starting m.room.power_levels check");
|
||||||
let mut creators = BTreeSet::new();
|
let mut creators = BTreeSet::new();
|
||||||
if room_version.authorization.explicitly_privilege_room_creators {
|
if room_version
|
||||||
|
.authorization
|
||||||
|
.explicitly_privilege_room_creators
|
||||||
|
{
|
||||||
creators.insert(create_event.sender().to_owned());
|
creators.insert(create_event.sender().to_owned());
|
||||||
for creator in room_create_content.additional_creators.iter().flatten() {
|
for creator in room_create_content.additional_creators.iter().flatten() {
|
||||||
creators.insert(creator.deserialize()?);
|
creators.insert(creator.deserialize()?);
|
||||||
@@ -710,7 +724,10 @@ where
|
|||||||
|
|
||||||
let mut creators = BTreeSet::new();
|
let mut creators = BTreeSet::new();
|
||||||
creators.insert(create_room.sender().to_owned());
|
creators.insert(create_room.sender().to_owned());
|
||||||
if room_version.authorization.explicitly_privilege_room_creators {
|
if room_version
|
||||||
|
.authorization
|
||||||
|
.explicitly_privilege_room_creators
|
||||||
|
{
|
||||||
// Explicitly privilege room creators
|
// Explicitly privilege room creators
|
||||||
// If the sender sent the create event, or in additional_creators, give them
|
// If the sender sent the create event, or in additional_creators, give them
|
||||||
// Int::MAX. Same case for target.
|
// Int::MAX. Same case for target.
|
||||||
@@ -878,7 +895,8 @@ where
|
|||||||
trace!(sender=%sender, "sender is invited or already joined to room, allowing join");
|
trace!(sender=%sender, "sender is invited or already joined to room, allowing join");
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
| JoinRule::KnockRestricted(_) if !room_version.authorization.knock_restricted_join_rule =>
|
| JoinRule::KnockRestricted(_)
|
||||||
|
if !room_version.authorization.knock_restricted_join_rule =>
|
||||||
{
|
{
|
||||||
warn!(
|
warn!(
|
||||||
"Join rule is knock_restricted but room version does not support it"
|
"Join rule is knock_restricted but room version does not support it"
|
||||||
@@ -1508,15 +1526,17 @@ fn verify_third_party_invite(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ruma::{events::{
|
use ruma::{
|
||||||
StateEventType, TimelineEventType,
|
events::{
|
||||||
room::{
|
StateEventType, TimelineEventType,
|
||||||
join_rules::{
|
room::{
|
||||||
AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent,
|
join_rules::{AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent},
|
||||||
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
},
|
},
|
||||||
member::{MembershipState, RoomMemberEventContent},
|
|
||||||
},
|
},
|
||||||
}, room::RoomMembership, room_version_rules::RoomVersionRules};
|
room::RoomMembership,
|
||||||
|
room_version_rules::RoomVersionRules,
|
||||||
|
};
|
||||||
use serde_json::value::to_raw_value as to_raw_json_value;
|
use serde_json::value::to_raw_value as to_raw_json_value;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ use ruma::{
|
|||||||
StateEventType, TimelineEventType,
|
StateEventType, TimelineEventType,
|
||||||
room::member::{MembershipState, RoomMemberEventContent},
|
room::member::{MembershipState, RoomMemberEventContent},
|
||||||
},
|
},
|
||||||
int, room_version_rules::{RoomIdFormatVersion, RoomVersionRules, StateResolutionVersion},
|
int,
|
||||||
|
room_version_rules::{RoomIdFormatVersion, RoomVersionRules, StateResolutionVersion},
|
||||||
};
|
};
|
||||||
use serde_json::from_str as from_json_str;
|
use serde_json::from_str as from_json_str;
|
||||||
|
|
||||||
pub(crate) use self::error::Error;
|
pub(crate) use self::error::Error;
|
||||||
use self::power_levels::PowerLevelsContentFields;
|
|
||||||
pub use self::event_auth::{auth_check, auth_types_for_event};
|
pub use self::event_auth::{auth_check, auth_types_for_event};
|
||||||
|
use self::power_levels::PowerLevelsContentFields;
|
||||||
use crate::{
|
use crate::{
|
||||||
debug, debug_error, err,
|
debug, debug_error, err,
|
||||||
matrix::{Event, StateKey},
|
matrix::{Event, StateKey},
|
||||||
@@ -106,19 +107,21 @@ where
|
|||||||
|
|
||||||
debug!(count = conflicting.len(), "conflicting events");
|
debug!(count = conflicting.len(), "conflicting events");
|
||||||
trace!(map = ?conflicting, "conflicting events");
|
trace!(map = ?conflicting, "conflicting events");
|
||||||
let (conflicted_state_subgraph, initial_state) =
|
let (conflicted_state_subgraph, initial_state) = if let StateResolutionVersion::V2(v2_rules) =
|
||||||
if let StateResolutionVersion::V2(v2_rules) = stateres_version && v2_rules.consider_conflicted_state_subgraph {
|
stateres_version
|
||||||
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
|
&& v2_rules.consider_conflicted_state_subgraph
|
||||||
.await
|
{
|
||||||
.ok_or_else(|| {
|
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
|
||||||
Error::InvalidPdu("Failed to calculate conflicted subgraph".to_owned())
|
.await
|
||||||
})?;
|
.ok_or_else(|| {
|
||||||
debug!(count = csg.len(), "conflicted subgraph");
|
Error::InvalidPdu("Failed to calculate conflicted subgraph".to_owned())
|
||||||
trace!(set = ?csg, "conflicted subgraph");
|
})?;
|
||||||
(csg, HashMap::new())
|
debug!(count = csg.len(), "conflicted subgraph");
|
||||||
} else {
|
trace!(set = ?csg, "conflicted subgraph");
|
||||||
(HashSet::new(), unconflicted.clone())
|
(csg, HashMap::new())
|
||||||
};
|
} else {
|
||||||
|
(HashSet::new(), unconflicted.clone())
|
||||||
|
};
|
||||||
|
|
||||||
// `all_conflicted` contains unique items
|
// `all_conflicted` contains unique items
|
||||||
// synapse says `full_set = {eid for eid in full_conflicted_set if eid in
|
// synapse says `full_set = {eid for eid in full_conflicted_set if eid in
|
||||||
@@ -974,10 +977,14 @@ mod tests {
|
|||||||
use maplit::{hashmap, hashset};
|
use maplit::{hashmap, hashset};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId, events::{
|
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId,
|
||||||
|
events::{
|
||||||
StateEventType, TimelineEventType,
|
StateEventType, TimelineEventType,
|
||||||
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||||
}, int, room_version_rules::RoomVersionRules, uint
|
},
|
||||||
|
int,
|
||||||
|
room_version_rules::RoomVersionRules,
|
||||||
|
uint,
|
||||||
};
|
};
|
||||||
use serde_json::{json, value::to_raw_value as to_raw_json_value};
|
use serde_json::{json, value::to_raw_value as to_raw_json_value};
|
||||||
|
|
||||||
@@ -1423,13 +1430,18 @@ mod tests {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let resolved =
|
let resolved = match super::resolve(
|
||||||
match super::resolve(&RoomVersionRules::V2, &state_sets, &auth_chain, &fetcher, &exists)
|
&RoomVersionRules::V2,
|
||||||
.await
|
&state_sets,
|
||||||
{
|
&auth_chain,
|
||||||
| Ok(state) => state,
|
&fetcher,
|
||||||
| Err(e) => panic!("{e}"),
|
&exists,
|
||||||
};
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
| Ok(state) => state,
|
||||||
|
| Err(e) => panic!("{e}"),
|
||||||
|
};
|
||||||
|
|
||||||
assert_eq!(expected, resolved);
|
assert_eq!(expected, resolved);
|
||||||
}
|
}
|
||||||
@@ -1536,13 +1548,18 @@ mod tests {
|
|||||||
|
|
||||||
let fetcher = |id: OwnedEventId| ready(ev_map.get(&id).cloned());
|
let fetcher = |id: OwnedEventId| ready(ev_map.get(&id).cloned());
|
||||||
let exists = |id: OwnedEventId| ready(ev_map.get(&id).is_some());
|
let exists = |id: OwnedEventId| ready(ev_map.get(&id).is_some());
|
||||||
let resolved =
|
let resolved = match super::resolve(
|
||||||
match super::resolve(&RoomVersionRules::V6, &state_sets, &auth_chain, &fetcher, &exists)
|
&RoomVersionRules::V6,
|
||||||
.await
|
&state_sets,
|
||||||
{
|
&auth_chain,
|
||||||
| Ok(state) => state,
|
&fetcher,
|
||||||
| Err(e) => panic!("{e}"),
|
&exists,
|
||||||
};
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
| Ok(state) => state,
|
||||||
|
| Err(e) => panic!("{e}"),
|
||||||
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
resolved = ?resolved
|
resolved = ?resolved
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
Int, OwnedUserId, UserId, events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent}, power_levels::{NotificationPowerLevels, default_power_level}, room_version_rules::{AuthorizationRules, RoomVersionRules}, serde::deserialize_v1_powerlevel
|
Int, OwnedUserId, UserId,
|
||||||
|
events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent},
|
||||||
|
power_levels::{NotificationPowerLevels, default_power_level},
|
||||||
|
room_version_rules::{AuthorizationRules, RoomVersionRules},
|
||||||
|
serde::deserialize_v1_powerlevel,
|
||||||
};
|
};
|
||||||
use super::serde_backports::*;
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::{Error, from_str as from_json_str};
|
use serde_json::{Error, from_str as from_json_str};
|
||||||
|
|
||||||
use super::Result;
|
use super::{Result, serde_backports::*};
|
||||||
use crate::error;
|
use crate::error;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -44,7 +47,10 @@ struct IntRoomPowerLevelsEventContent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl IntRoomPowerLevelsEventContent {
|
impl IntRoomPowerLevelsEventContent {
|
||||||
fn to_room_power_levels_content(self, auth_rules: &AuthorizationRules) -> RoomPowerLevelsEventContent {
|
fn to_room_power_levels_content(
|
||||||
|
self,
|
||||||
|
auth_rules: &AuthorizationRules,
|
||||||
|
) -> RoomPowerLevelsEventContent {
|
||||||
let IntRoomPowerLevelsEventContent {
|
let IntRoomPowerLevelsEventContent {
|
||||||
ban,
|
ban,
|
||||||
events,
|
events,
|
||||||
@@ -105,7 +111,10 @@ pub(crate) fn deserialize_power_levels(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_integer_power_levels(content: &str, auth_rules: &AuthorizationRules) -> Option<RoomPowerLevelsEventContent> {
|
fn deserialize_integer_power_levels(
|
||||||
|
content: &str,
|
||||||
|
auth_rules: &AuthorizationRules,
|
||||||
|
) -> Option<RoomPowerLevelsEventContent> {
|
||||||
match from_json_str::<IntRoomPowerLevelsEventContent>(content) {
|
match from_json_str::<IntRoomPowerLevelsEventContent>(content) {
|
||||||
| Ok(content) => Some(content.to_room_power_levels_content(auth_rules)),
|
| Ok(content) => Some(content.to_room_power_levels_content(auth_rules)),
|
||||||
| Err(_) => {
|
| Err(_) => {
|
||||||
|
|||||||
@@ -1,120 +1,120 @@
|
|||||||
//! These functions are copied from an old version of Ruma. power_levels.rs uses them to lazily deserialize power level events.
|
//! These functions are copied from an old version of Ruma. power_levels.rs uses
|
||||||
//! Upstream Ruma uses a much more elegant approach in its state resolution code, which we may want
|
//! them to lazily deserialize power level events. Upstream Ruma uses a much
|
||||||
|
//! more elegant approach in its state resolution code, which we may want
|
||||||
//! to look into at some point.
|
//! to look into at some point.
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::{fmt, marker::PhantomData};
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, de::{MapAccess, Visitor}};
|
|
||||||
use ruma::{Int, serde::deserialize_v1_powerlevel};
|
use ruma::{Int, serde::deserialize_v1_powerlevel};
|
||||||
|
use serde::{
|
||||||
|
Deserialize, Deserializer,
|
||||||
|
de::{MapAccess, Visitor},
|
||||||
|
};
|
||||||
|
|
||||||
/// Take a Map with values of either an integer number or a string and deserialize
|
/// Take a Map with values of either an integer number or a string and
|
||||||
/// those to integer numbers in a Vec of sorted pairs.
|
/// deserialize those to integer numbers in a Vec of sorted pairs.
|
||||||
///
|
///
|
||||||
/// To be used like this:
|
/// To be used like this:
|
||||||
/// `#[serde(deserialize_with = "vec_deserialize_v1_powerlevel_values")]`
|
/// `#[serde(deserialize_with = "vec_deserialize_v1_powerlevel_values")]`
|
||||||
pub fn vec_deserialize_v1_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
pub fn vec_deserialize_v1_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
T: Deserialize<'de> + Ord,
|
T: Deserialize<'de> + Ord,
|
||||||
{
|
{
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
struct IntWrap(Int);
|
struct IntWrap(Int);
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for IntWrap {
|
impl<'de> Deserialize<'de> for IntWrap {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
deserialize_v1_powerlevel(deserializer).map(IntWrap)
|
deserialize_v1_powerlevel(deserializer).map(IntWrap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct IntMapVisitor<T> {
|
struct IntMapVisitor<T> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IntMapVisitor<T> {
|
impl<T> IntMapVisitor<T> {
|
||||||
fn new() -> Self {
|
fn new() -> Self { Self { _phantom: PhantomData } }
|
||||||
Self { _phantom: PhantomData }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
||||||
where
|
where
|
||||||
T: Deserialize<'de> + Ord,
|
T: Deserialize<'de> + Ord,
|
||||||
{
|
{
|
||||||
type Value = Vec<(T, Int)>;
|
type Value = Vec<(T, Int)>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
formatter.write_str("a map with integers or strings as values")
|
formatter.write_str("a map with integers or strings as values")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
if let Some(hint) = map.size_hint() {
|
if let Some(hint) = map.size_hint() {
|
||||||
res.reserve(hint);
|
res.reserve(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some((k, IntWrap(v))) = map.next_entry()? {
|
while let Some((k, IntWrap(v))) = map.next_entry()? {
|
||||||
res.push((k, v));
|
res.push((k, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sort_unstable();
|
res.sort_unstable();
|
||||||
res.dedup_by(|a, b| a.0 == b.0);
|
res.dedup_by(|a, b| a.0 == b.0);
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
de.deserialize_map(IntMapVisitor::new())
|
de.deserialize_map(IntMapVisitor::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take a Map with integer values and deserialize those to a Vec of sorted pairs
|
/// Take a Map with integer values and deserialize those to a Vec of sorted
|
||||||
|
/// pairs
|
||||||
///
|
///
|
||||||
/// To be used like this:
|
/// To be used like this:
|
||||||
/// `#[serde(deserialize_with = "vec_deserialize_int_powerlevel_values")]`
|
/// `#[serde(deserialize_with = "vec_deserialize_int_powerlevel_values")]`
|
||||||
pub fn vec_deserialize_int_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
pub fn vec_deserialize_int_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
T: Deserialize<'de> + Ord,
|
T: Deserialize<'de> + Ord,
|
||||||
{
|
{
|
||||||
struct IntMapVisitor<T> {
|
struct IntMapVisitor<T> {
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> IntMapVisitor<T> {
|
impl<T> IntMapVisitor<T> {
|
||||||
fn new() -> Self {
|
fn new() -> Self { Self { _phantom: PhantomData } }
|
||||||
Self { _phantom: PhantomData }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
|
||||||
where
|
where
|
||||||
T: Deserialize<'de> + Ord,
|
T: Deserialize<'de> + Ord,
|
||||||
{
|
{
|
||||||
type Value = Vec<(T, Int)>;
|
type Value = Vec<(T, Int)>;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
formatter.write_str("a map with integers as values")
|
formatter.write_str("a map with integers as values")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
if let Some(hint) = map.size_hint() {
|
if let Some(hint) = map.size_hint() {
|
||||||
res.reserve(hint);
|
res.reserve(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
while let Some(item) = map.next_entry()? {
|
while let Some(item) = map.next_entry()? {
|
||||||
res.push(item);
|
res.push(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sort_unstable();
|
res.sort_unstable();
|
||||||
res.dedup_by(|a, b| a.0 == b.0);
|
res.dedup_by(|a, b| a.0 == b.0);
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
de.deserialize_map(IntMapVisitor::new())
|
de.deserialize_map(IntMapVisitor::new())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,18 @@ use std::{
|
|||||||
|
|
||||||
use futures::future::ready;
|
use futures::future::ready;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, ServerSignatures, UserId, event_id, events::{
|
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, ServerSignatures,
|
||||||
|
UserId, event_id,
|
||||||
|
events::{
|
||||||
TimelineEventType,
|
TimelineEventType,
|
||||||
room::{
|
room::{
|
||||||
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||||
member::{MembershipState, RoomMemberEventContent},
|
member::{MembershipState, RoomMemberEventContent},
|
||||||
},
|
},
|
||||||
}, int, room_id, room_version_rules::RoomVersionRules, uint, user_id
|
},
|
||||||
|
int, room_id,
|
||||||
|
room_version_rules::RoomVersionRules,
|
||||||
|
uint, user_id,
|
||||||
};
|
};
|
||||||
use serde_json::{
|
use serde_json::{
|
||||||
json,
|
json,
|
||||||
@@ -130,9 +135,14 @@ pub(crate) async fn do_check(
|
|||||||
let event_map = &event_map;
|
let event_map = &event_map;
|
||||||
let fetch = |id: OwnedEventId| ready(event_map.get(&id).cloned());
|
let fetch = |id: OwnedEventId| ready(event_map.get(&id).cloned());
|
||||||
let exists = |id: OwnedEventId| ready(event_map.get(&id).is_some());
|
let exists = |id: OwnedEventId| ready(event_map.get(&id).is_some());
|
||||||
let resolved =
|
let resolved = super::resolve(
|
||||||
super::resolve(&RoomVersionRules::V6, state_sets, &auth_chain_sets, &fetch, &exists)
|
&RoomVersionRules::V6,
|
||||||
.await;
|
state_sets,
|
||||||
|
&auth_chain_sets,
|
||||||
|
&fetch,
|
||||||
|
&exists,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
match resolved {
|
match resolved {
|
||||||
| Ok(state) => state,
|
| Ok(state) => state,
|
||||||
|
|||||||
+38
-38
@@ -1,44 +1,44 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
pub fn versions() -> Vec<String> {
|
pub fn versions() -> Vec<String> {
|
||||||
vec![
|
vec![
|
||||||
"r0.0.1".to_owned(),
|
"r0.0.1".to_owned(),
|
||||||
"r0.1.0".to_owned(),
|
"r0.1.0".to_owned(),
|
||||||
"r0.2.0".to_owned(),
|
"r0.2.0".to_owned(),
|
||||||
"r0.3.0".to_owned(),
|
"r0.3.0".to_owned(),
|
||||||
"r0.4.0".to_owned(),
|
"r0.4.0".to_owned(),
|
||||||
"r0.5.0".to_owned(),
|
"r0.5.0".to_owned(),
|
||||||
"r0.6.0".to_owned(),
|
"r0.6.0".to_owned(),
|
||||||
"r0.6.1".to_owned(),
|
"r0.6.1".to_owned(),
|
||||||
"v1.1".to_owned(),
|
"v1.1".to_owned(),
|
||||||
"v1.2".to_owned(),
|
"v1.2".to_owned(),
|
||||||
"v1.3".to_owned(),
|
"v1.3".to_owned(),
|
||||||
"v1.4".to_owned(),
|
"v1.4".to_owned(),
|
||||||
"v1.5".to_owned(),
|
"v1.5".to_owned(),
|
||||||
"v1.8".to_owned(),
|
"v1.8".to_owned(),
|
||||||
"v1.11".to_owned(),
|
"v1.11".to_owned(),
|
||||||
"v1.12".to_owned(),
|
"v1.12".to_owned(),
|
||||||
"v1.13".to_owned(),
|
"v1.13".to_owned(),
|
||||||
"v1.14".to_owned(),
|
"v1.14".to_owned(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unstable_features() -> BTreeMap<String, bool> {
|
pub fn unstable_features() -> BTreeMap<String, bool> {
|
||||||
BTreeMap::from_iter([
|
BTreeMap::from_iter([
|
||||||
("org.matrix.e2e_cross_signing".to_owned(), true),
|
("org.matrix.e2e_cross_signing".to_owned(), true),
|
||||||
("org.matrix.msc2285.stable".to_owned(), true), /* private read receipts (https://github.com/matrix-org/matrix-spec-proposals/pull/2285) */
|
("org.matrix.msc2285.stable".to_owned(), true), /* private read receipts (https://github.com/matrix-org/matrix-spec-proposals/pull/2285) */
|
||||||
("uk.half-shot.msc2666.query_mutual_rooms".to_owned(), true), /* query mutual rooms (https://github.com/matrix-org/matrix-spec-proposals/pull/2666) */
|
("uk.half-shot.msc2666.query_mutual_rooms".to_owned(), true), /* query mutual rooms (https://github.com/matrix-org/matrix-spec-proposals/pull/2666) */
|
||||||
("org.matrix.msc2836".to_owned(), true), /* threading/threads (https://github.com/matrix-org/matrix-spec-proposals/pull/2836) */
|
("org.matrix.msc2836".to_owned(), true), /* threading/threads (https://github.com/matrix-org/matrix-spec-proposals/pull/2836) */
|
||||||
("org.matrix.msc2946".to_owned(), true), /* spaces/hierarchy summaries (https://github.com/matrix-org/matrix-spec-proposals/pull/2946) */
|
("org.matrix.msc2946".to_owned(), true), /* spaces/hierarchy summaries (https://github.com/matrix-org/matrix-spec-proposals/pull/2946) */
|
||||||
("org.matrix.msc3026.busy_presence".to_owned(), true), /* busy presence status (https://github.com/matrix-org/matrix-spec-proposals/pull/3026) */
|
("org.matrix.msc3026.busy_presence".to_owned(), true), /* busy presence status (https://github.com/matrix-org/matrix-spec-proposals/pull/3026) */
|
||||||
("org.matrix.msc3827".to_owned(), true), /* filtering of /publicRooms by room type (https://github.com/matrix-org/matrix-spec-proposals/pull/3827) */
|
("org.matrix.msc3827".to_owned(), true), /* filtering of /publicRooms by room type (https://github.com/matrix-org/matrix-spec-proposals/pull/3827) */
|
||||||
("org.matrix.msc3952_intentional_mentions".to_owned(), true), /* intentional mentions (https://github.com/matrix-org/matrix-spec-proposals/pull/3952) */
|
("org.matrix.msc3952_intentional_mentions".to_owned(), true), /* intentional mentions (https://github.com/matrix-org/matrix-spec-proposals/pull/3952) */
|
||||||
("org.matrix.msc3916.stable".to_owned(), true), /* authenticated media (https://github.com/matrix-org/matrix-spec-proposals/pull/3916) */
|
("org.matrix.msc3916.stable".to_owned(), true), /* authenticated media (https://github.com/matrix-org/matrix-spec-proposals/pull/3916) */
|
||||||
("org.matrix.msc4180".to_owned(), true), /* stable flag for 3916 (https://github.com/matrix-org/matrix-spec-proposals/pull/4180) */
|
("org.matrix.msc4180".to_owned(), true), /* stable flag for 3916 (https://github.com/matrix-org/matrix-spec-proposals/pull/4180) */
|
||||||
("uk.tcpip.msc4133".to_owned(), true), /* Extending User Profile API with Key:Value Pairs (https://github.com/matrix-org/matrix-spec-proposals/pull/4133) */
|
("uk.tcpip.msc4133".to_owned(), true), /* Extending User Profile API with Key:Value Pairs (https://github.com/matrix-org/matrix-spec-proposals/pull/4133) */
|
||||||
("us.cloke.msc4175".to_owned(), true), /* Profile field for user time zone (https://github.com/matrix-org/matrix-spec-proposals/pull/4175) */
|
("us.cloke.msc4175".to_owned(), true), /* Profile field for user time zone (https://github.com/matrix-org/matrix-spec-proposals/pull/4175) */
|
||||||
("org.matrix.simplified_msc3575".to_owned(), true), /* Simplified Sliding sync (https://github.com/matrix-org/matrix-spec-proposals/pull/4186) */
|
("org.matrix.simplified_msc3575".to_owned(), true), /* Simplified Sliding sync (https://github.com/matrix-org/matrix-spec-proposals/pull/4186) */
|
||||||
("uk.timedout.msc4323".to_owned(), true), /* agnostic suspend (https://github.com/matrix-org/matrix-spec-proposals/pull/4323) */
|
("uk.timedout.msc4323".to_owned(), true), /* agnostic suspend (https://github.com/matrix-org/matrix-spec-proposals/pull/4323) */
|
||||||
("org.matrix.msc4155".to_owned(), true), /* invite filtering (https://github.com/matrix-org/matrix-spec-proposals/pull/4155) */
|
("org.matrix.msc4155".to_owned(), true), /* invite filtering (https://github.com/matrix-org/matrix-spec-proposals/pull/4155) */
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-3
@@ -38,9 +38,7 @@ pub use info::{
|
|||||||
version,
|
version,
|
||||||
version::{name, version},
|
version::{name, version},
|
||||||
};
|
};
|
||||||
pub use matrix::{
|
pub use matrix::{Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, pdu, state_res};
|
||||||
Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, pdu, state_res,
|
|
||||||
};
|
|
||||||
pub use parking_lot::{Mutex as SyncMutex, RwLock as SyncRwLock};
|
pub use parking_lot::{Mutex as SyncMutex, RwLock as SyncRwLock};
|
||||||
pub use server::Server;
|
pub use server::Server;
|
||||||
pub use utils::{implement, result, result::Result};
|
pub use utils::{implement, result, result::Result};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::{fmt, marker::PhantomData, str::FromStr};
|
use std::{fmt, marker::PhantomData, str::FromStr};
|
||||||
|
|
||||||
use ruma::{CanonicalJsonError, CanonicalJsonObject, canonical_json::try_from_json_map};
|
use ruma::{CanonicalJsonError, CanonicalJsonObject, canonical_json::to_canonical_value};
|
||||||
|
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
@@ -11,12 +11,12 @@ use crate::Result;
|
|||||||
pub fn to_canonical_object<T: serde::Serialize>(
|
pub fn to_canonical_object<T: serde::Serialize>(
|
||||||
value: T,
|
value: T,
|
||||||
) -> Result<CanonicalJsonObject, CanonicalJsonError> {
|
) -> Result<CanonicalJsonObject, CanonicalJsonError> {
|
||||||
use CanonicalJsonError::SerDe;
|
use ruma::CanonicalJsonValue;
|
||||||
use serde::ser::Error;
|
|
||||||
|
|
||||||
match serde_json::to_value(value).map_err(SerDe)? {
|
match to_canonical_value(value)? {
|
||||||
| serde_json::Value::Object(map) => try_from_json_map(map),
|
| CanonicalJsonValue::Object(map) => Ok(map),
|
||||||
| _ => Err(SerDe(serde_json::Error::custom("Value must be an object"))),
|
| _ => Err(to_canonical_value(1.0_f32).unwrap_err()), /* Hack to return a
|
||||||
|
* CanonicalJsonError */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ use std::{borrow::Cow, fmt::Debug};
|
|||||||
|
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
arrayvec::ArrayVec,
|
arrayvec::ArrayVec,
|
||||||
ruma::{EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, room_id, serde::Raw, user_id},
|
ruma::{
|
||||||
|
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, room_id, serde::Raw,
|
||||||
|
user_id,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -439,7 +442,8 @@ fn serde_tuple_option_some_some() {
|
|||||||
aa.push(0xFF);
|
aa.push(0xFF);
|
||||||
aa.extend_from_slice(user_id.as_bytes());
|
aa.extend_from_slice(user_id.as_bytes());
|
||||||
|
|
||||||
let bb: (Option<OwnedRoomId>, Option<OwnedUserId>) = (Some(room_id.to_owned()), Some(user_id.to_owned()));
|
let bb: (Option<OwnedRoomId>, Option<OwnedUserId>) =
|
||||||
|
(Some(room_id.to_owned()), Some(user_id.to_owned()));
|
||||||
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
|
||||||
assert_eq!(aa, bbs);
|
assert_eq!(aa, bbs);
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ http-body-util.workspace = true
|
|||||||
hyper.workspace = true
|
hyper.workspace = true
|
||||||
hyper-util.workspace = true
|
hyper-util.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
assign.workspace = true
|
||||||
ruma.workspace = true
|
ruma.workspace = true
|
||||||
rustls.workspace = true
|
rustls.workspace = true
|
||||||
rustls.optional = true
|
rustls.optional = true
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use axum::{Router, response::IntoResponse};
|
|||||||
use conduwuit::Error;
|
use conduwuit::Error;
|
||||||
use conduwuit_service::{Services, state, state::Guard};
|
use conduwuit_service::{Services, state, state::Guard};
|
||||||
use http::{StatusCode, Uri};
|
use http::{StatusCode, Uri};
|
||||||
use ruma::api::client::error::ErrorKind;
|
use ruma::api::error::ErrorKind;
|
||||||
|
|
||||||
pub(crate) fn build(services: &Arc<Services>) -> (Router, Guard) {
|
pub(crate) fn build(services: &Arc<Services>) -> (Router, Guard) {
|
||||||
let router = Router::<state::State>::new();
|
let router = Router::<state::State>::new();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ unstable-msc3202 = []
|
|||||||
unstable-msc4203 = []
|
unstable-msc4203 = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
assign.workspace = true
|
||||||
ruma.workspace = true
|
ruma.workspace = true
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
pub mod rooms;
|
pub mod rooms;
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
use ruma::{OwnedRoomAliasId, OwnedRoomId, OwnedUserId, api::{auth_scheme::AccessToken, request, response}, metadata};
|
use ruma::{
|
||||||
|
OwnedRoomAliasId, OwnedRoomId, OwnedUserId,
|
||||||
|
api::{auth_scheme::AccessToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: PUT,
|
method: PUT,
|
||||||
@@ -16,10 +20,10 @@ pub mod v1 {
|
|||||||
pub room_id: OwnedRoomId,
|
pub room_id: OwnedRoomId,
|
||||||
|
|
||||||
/// Whether to ban (true) or unban (false) the room.
|
/// Whether to ban (true) or unban (false) the room.
|
||||||
/// If true, and the room is not banned, all local users will be evacuated
|
/// If true, and the room is not banned, all local users will be
|
||||||
/// and prevented from re-joining.
|
/// evacuated and prevented from re-joining.
|
||||||
/// If false, and the room is unbanned, local users will be allowed to re-join.
|
/// If false, and the room is unbanned, local users will be allowed to
|
||||||
/// No-ops are no-ops.
|
/// re-join. No-ops are no-ops.
|
||||||
pub banned: bool,
|
pub banned: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,24 +31,26 @@ pub mod v1 {
|
|||||||
pub struct Response {
|
pub struct Response {
|
||||||
pub kicked_users: Vec<OwnedUserId>,
|
pub kicked_users: Vec<OwnedUserId>,
|
||||||
pub failed_kicked_users: Vec<OwnedUserId>,
|
pub failed_kicked_users: Vec<OwnedUserId>,
|
||||||
pub local_aliases: Vec<OwnedRoomAliasId>
|
pub local_aliases: Vec<OwnedRoomAliasId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(room_id: OwnedRoomId, banned: bool) -> Self {
|
pub fn new(room_id: OwnedRoomId, banned: bool) -> Self { Self { room_id, banned } }
|
||||||
Self { room_id, banned }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
kicked_users: Vec<OwnedUserId>,
|
kicked_users: Vec<OwnedUserId>,
|
||||||
failed_kicked_users: Vec<OwnedUserId>,
|
failed_kicked_users: Vec<OwnedUserId>,
|
||||||
local_aliases: Vec<OwnedRoomAliasId>,
|
local_aliases: Vec<OwnedRoomAliasId>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { kicked_users, failed_kicked_users, local_aliases }
|
Self {
|
||||||
|
kicked_users,
|
||||||
|
failed_kicked_users,
|
||||||
|
local_aliases,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, api::{auth_scheme::AccessToken, request, response}, metadata
|
OwnedRoomId,
|
||||||
|
api::{auth_scheme::AccessToken, request, response},
|
||||||
|
metadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
@@ -22,16 +24,12 @@ pub mod v1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(rooms: Vec<OwnedRoomId>) -> Self {
|
pub fn new(rooms: Vec<OwnedRoomId>) -> Self { Self { rooms } }
|
||||||
Self { rooms }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
pub mod list;
|
|
||||||
pub mod ban;
|
pub mod ban;
|
||||||
|
pub mod list;
|
||||||
|
|||||||
@@ -3,51 +3,50 @@
|
|||||||
//! Check the suspension status of a target user
|
//! Check the suspension status of a target user
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}` ([msc])
|
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}`
|
||||||
//!
|
//! ([msc])
|
||||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
//!
|
||||||
|
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedUserId, api::{auth_scheme::AccessToken, request, response}, metadata
|
OwnedUserId,
|
||||||
};
|
api::{auth_scheme::AccessToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: GET,
|
method: GET,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: AccessToken,
|
authentication: AccessToken,
|
||||||
history: {
|
history: {
|
||||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the get & set user suspension status endpoint.
|
/// Request type for the get & set user suspension status endpoint.
|
||||||
#[request(error = ruma::api::client::Error)]
|
#[request(error = ruma::api::error::Error)]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The user to look up.
|
/// The user to look up.
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub user_id: OwnedUserId,
|
pub user_id: OwnedUserId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the suspension endpoints
|
/// Response type for the suspension endpoints
|
||||||
#[response(error = ruma::api::client::Error)]
|
#[response(error = ruma::api::error::Error)]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
/// Whether the user is currently suspended.
|
/// Whether the user is currently suspended.
|
||||||
pub suspended: bool,
|
pub suspended: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new `Request` with the given user id.
|
/// Creates a new `Request` with the given user id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(user_id: OwnedUserId) -> Self {
|
pub fn new(user_id: OwnedUserId) -> Self { Self { user_id } }
|
||||||
Self { user_id }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new `Response` with the given suspension status.
|
/// Creates a new `Response` with the given suspension status.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(suspended: bool) -> Self {
|
pub fn new(suspended: bool) -> Self { Self { suspended } }
|
||||||
Self { suspended }
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
pub mod continuwuity;
|
pub mod continuwuity;
|
||||||
pub mod get_suspended;
|
pub mod get_suspended;
|
||||||
pub mod set_suspended;
|
pub mod set_suspended;
|
||||||
|
|||||||
@@ -3,53 +3,52 @@
|
|||||||
//! Set the suspension status of a target user
|
//! Set the suspension status of a target user
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}` ([msc])
|
//! `/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{userID}`
|
||||||
//!
|
//! ([msc])
|
||||||
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
//!
|
||||||
|
//! [msc]: https://github.com/matrix-org/matrix-spec-proposals/pull/4323
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedUserId, api::{auth_scheme::AccessToken, request, response}, metadata
|
OwnedUserId,
|
||||||
};
|
api::{auth_scheme::AccessToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: PUT,
|
method: PUT,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: AccessToken,
|
authentication: AccessToken,
|
||||||
history: {
|
history: {
|
||||||
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
unstable => "/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/{user_id}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the set user suspension status endpoint.
|
/// Request type for the set user suspension status endpoint.
|
||||||
#[request(error = ruma::api::client::Error)]
|
#[request(error = ruma::api::error::Error)]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The user to look up.
|
/// The user to look up.
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub user_id: OwnedUserId,
|
pub user_id: OwnedUserId,
|
||||||
|
|
||||||
pub suspended: bool,
|
pub suspended: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the suspension endpoints
|
/// Response type for the suspension endpoints
|
||||||
#[response(error = ruma::api::client::Error)]
|
#[response(error = ruma::api::error::Error)]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
/// Whether the user is currently suspended.
|
/// Whether the user is currently suspended.
|
||||||
pub suspended: bool,
|
pub suspended: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new `Request` with the given user id.
|
/// Creates a new `Request` with the given user id.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(user_id: OwnedUserId, suspended: bool) -> Self {
|
pub fn new(user_id: OwnedUserId, suspended: bool) -> Self { Self { user_id, suspended } }
|
||||||
Self { user_id, suspended }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new `Response` with the given suspension status.
|
/// Creates a new `Response` with the given suspension status.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(suspended: bool) -> Self {
|
pub fn new(suspended: bool) -> Self { Self { suspended } }
|
||||||
Self { suspended }
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
pub mod user_may_invite;
|
pub mod user_may_invite;
|
||||||
pub mod user_may_join_room;
|
pub mod user_may_join_room;
|
||||||
|
|||||||
@@ -1,50 +1,51 @@
|
|||||||
//! `POST /api/1/spam_check/user_may_invite`
|
//! `POST /api/1/spam_check/user_may_invite`
|
||||||
//!
|
//!
|
||||||
//! Checks that a user may invite the given user to the given room via Draupnir anti-spam
|
//! Checks that a user may invite the given user to the given room via Draupnir
|
||||||
|
//! anti-spam
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
OwnedRoomId, OwnedUserId,
|
||||||
};
|
api::{auth_scheme::AppserviceToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: AppserviceToken,
|
authentication: AppserviceToken,
|
||||||
history: {
|
history: {
|
||||||
1.0 => "/api/1/spam_check/user_may_invite",
|
1.0 => "/api/1/spam_check/user_may_invite",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the `user_may_invite` callback.
|
/// Request type for the `user_may_invite` callback.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The room the invitee is being invited to
|
/// The room the invitee is being invited to
|
||||||
pub room_id: OwnedRoomId,
|
pub room_id: OwnedRoomId,
|
||||||
/// The user sending the invite
|
/// The user sending the invite
|
||||||
pub inviter: OwnedUserId,
|
pub inviter: OwnedUserId,
|
||||||
/// The user being invited
|
/// The user being invited
|
||||||
pub invitee: OwnedUserId,
|
pub invitee: OwnedUserId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `user_may_invite` callback.
|
/// Response type for the `user_may_invite` callback.
|
||||||
#[response]
|
#[response]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Response;
|
pub struct Response;
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new empty `Request`.
|
/// Creates a new empty `Request`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(room_id: OwnedRoomId, inviter: OwnedUserId, invitee: OwnedUserId) -> Self {
|
pub fn new(room_id: OwnedRoomId, inviter: OwnedUserId, invitee: OwnedUserId) -> Self {
|
||||||
Self { room_id, inviter, invitee }
|
Self { room_id, inviter, invitee }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new empty `Response`.
|
/// Creates a new empty `Response`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,51 @@
|
|||||||
//! `POST /api/1/spam_check/user_may_join_room`
|
//! `POST /api/1/spam_check/user_may_join_room`
|
||||||
//!
|
//!
|
||||||
//! Endpoint that checks whether a user may join a given room via Draupnir anti-spam
|
//! Endpoint that checks whether a user may join a given room via Draupnir
|
||||||
|
//! anti-spam
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
OwnedRoomId, OwnedUserId,
|
||||||
};
|
api::{auth_scheme::AppserviceToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: AppserviceToken,
|
authentication: AppserviceToken,
|
||||||
history: {
|
history: {
|
||||||
1.0 => "/api/1/spam_check/user_may_join_room",
|
1.0 => "/api/1/spam_check/user_may_join_room",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the `user_may_join_room` callback.
|
/// Request type for the `user_may_join_room` callback.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The user trying to join a room
|
/// The user trying to join a room
|
||||||
pub user: OwnedUserId,
|
pub user: OwnedUserId,
|
||||||
/// The room the user is trying to join
|
/// The room the user is trying to join
|
||||||
pub room: OwnedRoomId,
|
pub room: OwnedRoomId,
|
||||||
/// Whether the user was invited to this room
|
/// Whether the user was invited to this room
|
||||||
pub is_invited: bool,
|
pub is_invited: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `user_may_join_room` callback.
|
/// Response type for the `user_may_join_room` callback.
|
||||||
#[response]
|
#[response]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Response;
|
pub struct Response;
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new empty `Request`.
|
/// Creates a new empty `Request`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(user: OwnedUserId, room: OwnedRoomId, is_invited: bool) -> Self {
|
pub fn new(user: OwnedUserId, room: OwnedRoomId, is_invited: bool) -> Self {
|
||||||
Self { user, room, is_invited }
|
Self { user, room, is_invited }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new empty `Response`.
|
/// Creates a new empty `Response`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,247 +1,271 @@
|
|||||||
//! Types for invite filtering ([MSC4155]).
|
//! Types for invite filtering ([MSC4155]).
|
||||||
//!
|
//!
|
||||||
//! MSC4155: https://github.com/matrix-org/matrix-spec-proposals/pull/4155
|
//! MSC4155: https://github.com/matrix-org/matrix-spec-proposals/pull/4155
|
||||||
|
|
||||||
use ruma::{ServerName, UserId};
|
use ruma::{ServerName, UserId, exports::ruma_macros::EventContent};
|
||||||
use ruma::exports::ruma_macros::EventContent;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use wildmatch::WildMatch;
|
use wildmatch::WildMatch;
|
||||||
|
|
||||||
/// Represents a user's level of filtering on actions from another user or server.
|
/// Represents a user's level of filtering on actions from another user or
|
||||||
/// "Ignore" and "block" are defined in [MSC4283].
|
/// server. "Ignore" and "block" are defined in [MSC4283].
|
||||||
///
|
///
|
||||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum FilterLevel {
|
pub enum FilterLevel {
|
||||||
Allow,
|
Allow,
|
||||||
Ignore,
|
Ignore,
|
||||||
Block,
|
Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, EventContent)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[ruma_event(type = "m.invite_permission_config", kind = GlobalAccountData)]
|
#[ruma_event(type = "m.invite_permission_config", kind = GlobalAccountData)]
|
||||||
pub struct InvitePermissionConfigEventContent {
|
pub struct InvitePermissionConfigEventContent {
|
||||||
/// A global on/off toggle for all rules
|
/// A global on/off toggle for all rules
|
||||||
#[serde(default = "ruma::serde::default_true")]
|
#[serde(default = "ruma::serde::default_true")]
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
|
|
||||||
/// A list of globs matching users which are allowed to send an invite.
|
|
||||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub allowed_users: Vec<String>,
|
|
||||||
/// A list of globs matching users whose invites should be ignored (as defined in [MSC4283]).
|
|
||||||
///
|
|
||||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub ignored_users: Vec<String>,
|
|
||||||
/// A list of globs matching users whose invites should be blocked (as defined in [MSC4283]).
|
|
||||||
/// Invites from blocked users should be refused with the M_INVITE_BLOCKED status code.
|
|
||||||
///
|
|
||||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
||||||
pub blocked_users: Vec<String>,
|
|
||||||
|
|
||||||
|
/// A list of globs matching users which are allowed to send an invite.
|
||||||
|
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub allowed_users: Vec<String>,
|
||||||
|
/// A list of globs matching users whose invites should be ignored (as
|
||||||
|
/// defined in [MSC4283]).
|
||||||
|
///
|
||||||
|
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub ignored_users: Vec<String>,
|
||||||
|
/// A list of globs matching users whose invites should be blocked (as
|
||||||
|
/// defined in [MSC4283]). Invites from blocked users should be refused
|
||||||
|
/// with the M_INVITE_BLOCKED status code.
|
||||||
|
///
|
||||||
|
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub blocked_users: Vec<String>,
|
||||||
|
|
||||||
/// A list of globs matching servers which are allowed to send an invite.
|
/// A list of globs matching servers which are allowed to send an invite.
|
||||||
/// Entries in this list supersede entries in the ignored and blocked lists.
|
/// Entries in this list supersede entries in the ignored and blocked lists.
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
pub allowed_servers: Vec<String>,
|
pub allowed_servers: Vec<String>,
|
||||||
/// A list of globs matching servers whose invites should be ignored (as defined in [MSC4283]).
|
/// A list of globs matching servers whose invites should be ignored (as
|
||||||
///
|
/// defined in [MSC4283]).
|
||||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
///
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||||
pub ignored_servers: Vec<String>,
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
/// A list of globs matching servers whose invites should be blocked (as defined in [MSC4283]).
|
pub ignored_servers: Vec<String>,
|
||||||
/// Invites from blocked servers should be refused with the M_INVITE_BLOCKED status code.
|
/// A list of globs matching servers whose invites should be blocked (as
|
||||||
///
|
/// defined in [MSC4283]). Invites from blocked servers should be refused
|
||||||
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
/// with the M_INVITE_BLOCKED status code.
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
///
|
||||||
pub blocked_servers: Vec<String>,
|
/// MSC4283: https://github.com/matrix-org/matrix-spec-proposals/pull/4283
|
||||||
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
pub blocked_servers: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InvitePermissionConfigEventContent {
|
impl InvitePermissionConfigEventContent {
|
||||||
/// Creates a new `InvitePermissionConfigEventContent` from six lists of globs.
|
/// Creates a new `InvitePermissionConfigEventContent` from six lists of
|
||||||
#[must_use]
|
/// globs.
|
||||||
pub fn new(
|
#[must_use]
|
||||||
enabled: bool,
|
pub fn new(
|
||||||
allowed_users: Vec<String>,
|
enabled: bool,
|
||||||
ignored_users: Vec<String>,
|
allowed_users: Vec<String>,
|
||||||
blocked_users: Vec<String>,
|
ignored_users: Vec<String>,
|
||||||
allowed_servers: Vec<String>,
|
blocked_users: Vec<String>,
|
||||||
ignored_servers: Vec<String>,
|
allowed_servers: Vec<String>,
|
||||||
blocked_servers: Vec<String>,
|
ignored_servers: Vec<String>,
|
||||||
) -> Self {
|
blocked_servers: Vec<String>,
|
||||||
Self {
|
) -> Self {
|
||||||
enabled,
|
Self {
|
||||||
allowed_users,
|
enabled,
|
||||||
ignored_users,
|
allowed_users,
|
||||||
blocked_users,
|
ignored_users,
|
||||||
allowed_servers,
|
blocked_users,
|
||||||
ignored_servers,
|
allowed_servers,
|
||||||
blocked_servers,
|
ignored_servers,
|
||||||
}
|
blocked_servers,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Test the filters against a user id. This function will check both the
|
/// Test the filters against a user id. This function will check both the
|
||||||
/// user rules _and_ the server rules.
|
/// user rules _and_ the server rules.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(clippy::if_same_then_else)]
|
#[allow(clippy::if_same_then_else)]
|
||||||
pub fn user_filter_level(&self, user: &UserId) -> FilterLevel {
|
pub fn user_filter_level(&self, user: &UserId) -> FilterLevel {
|
||||||
if !self.enabled {
|
if !self.enabled {
|
||||||
FilterLevel::Allow
|
FilterLevel::Allow
|
||||||
} else if Self::matches(&self.allowed_users, user.as_str()) {
|
} else if Self::matches(&self.allowed_users, user.as_str()) {
|
||||||
FilterLevel::Allow
|
FilterLevel::Allow
|
||||||
} else if Self::matches(&self.ignored_users, user.as_str()) {
|
} else if Self::matches(&self.ignored_users, user.as_str()) {
|
||||||
FilterLevel::Ignore
|
FilterLevel::Ignore
|
||||||
} else if Self::matches(&self.blocked_users, user.as_str()) {
|
} else if Self::matches(&self.blocked_users, user.as_str()) {
|
||||||
FilterLevel::Block
|
FilterLevel::Block
|
||||||
} else {
|
} else {
|
||||||
self.server_filter_level(user.server_name())
|
self.server_filter_level(user.server_name())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test the filters against a server name. Port numbers are ignored.
|
/// Test the filters against a server name. Port numbers are ignored.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn server_filter_level(&self, server: &ServerName) -> FilterLevel {
|
pub fn server_filter_level(&self, server: &ServerName) -> FilterLevel {
|
||||||
if !self.enabled {
|
if !self.enabled {
|
||||||
FilterLevel::Allow
|
FilterLevel::Allow
|
||||||
} else {
|
} else {
|
||||||
let server = server.host();
|
let server = server.host();
|
||||||
if Self::matches(&self.allowed_servers, server) {
|
if Self::matches(&self.allowed_servers, server) {
|
||||||
FilterLevel::Allow
|
FilterLevel::Allow
|
||||||
} else if Self::matches(&self.ignored_servers, server) {
|
} else if Self::matches(&self.ignored_servers, server) {
|
||||||
FilterLevel::Ignore
|
FilterLevel::Ignore
|
||||||
} else if Self::matches(&self.blocked_servers, server) {
|
} else if Self::matches(&self.blocked_servers, server) {
|
||||||
FilterLevel::Block
|
FilterLevel::Block
|
||||||
} else {
|
} else {
|
||||||
FilterLevel::Allow
|
FilterLevel::Allow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches(a: &[String], s: &str) -> bool {
|
fn matches(a: &[String], s: &str) -> bool {
|
||||||
a.iter().map(String::as_str).any(|a| WildMatch::new(a).matches(s))
|
a.iter()
|
||||||
}
|
.map(String::as_str)
|
||||||
|
.any(|a| WildMatch::new(a).matches(s))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ruma::{ServerName, UserId, events::GlobalAccountDataEvent};
|
use ruma::{ServerName, UserId, events::GlobalAccountDataEvent};
|
||||||
use serde_json::{from_value as from_json_value, json};
|
use serde_json::{from_value as from_json_value, json};
|
||||||
|
|
||||||
use crate::invite_permission_config::{FilterLevel, InvitePermissionConfigEventContent};
|
use crate::invite_permission_config::{FilterLevel, InvitePermissionConfigEventContent};
|
||||||
|
|
||||||
fn user_id(id: &str) -> &UserId {
|
fn user_id(id: &str) -> &UserId { <&UserId>::try_from(id).unwrap() }
|
||||||
<&UserId>::try_from(id).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn server_name(name: &str) -> &ServerName {
|
fn server_name(name: &str) -> &ServerName { <&ServerName>::try_from(name).unwrap() }
|
||||||
<&ServerName>::try_from(name).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn default_values() {
|
fn default_values() {
|
||||||
let data = json!({
|
let data = json!({
|
||||||
"content": {},
|
"content": {},
|
||||||
"type": "org.matrix.msc4155.invite_permission_config"
|
"type": "org.matrix.msc4155.invite_permission_config"
|
||||||
});
|
});
|
||||||
|
|
||||||
let event: GlobalAccountDataEvent<InvitePermissionConfigEventContent> = from_json_value(data).unwrap();
|
let event: GlobalAccountDataEvent<InvitePermissionConfigEventContent> =
|
||||||
assert!(event.content.enabled);
|
from_json_value(data).unwrap();
|
||||||
assert!(event.content.allowed_users.is_empty());
|
assert!(event.content.enabled);
|
||||||
assert!(event.content.ignored_users.is_empty());
|
assert!(event.content.allowed_users.is_empty());
|
||||||
assert!(event.content.blocked_users.is_empty());
|
assert!(event.content.ignored_users.is_empty());
|
||||||
assert!(event.content.allowed_servers.is_empty());
|
assert!(event.content.blocked_users.is_empty());
|
||||||
assert!(event.content.ignored_servers.is_empty());
|
assert!(event.content.allowed_servers.is_empty());
|
||||||
assert!(event.content.blocked_servers.is_empty());
|
assert!(event.content.ignored_servers.is_empty());
|
||||||
assert_eq!(event.content.user_filter_level(user_id("@alice:example.com")), FilterLevel::Allow);
|
assert!(event.content.blocked_servers.is_empty());
|
||||||
assert_eq!(event.content.server_filter_level(server_name("example.com")), FilterLevel::Allow);
|
assert_eq!(
|
||||||
}
|
event
|
||||||
|
.content
|
||||||
|
.user_filter_level(user_id("@alice:example.com")),
|
||||||
|
FilterLevel::Allow
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
event
|
||||||
|
.content
|
||||||
|
.server_filter_level(server_name("example.com")),
|
||||||
|
FilterLevel::Allow
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_the_world() {
|
fn block_the_world() {
|
||||||
let event = InvitePermissionConfigEventContent {
|
let event = InvitePermissionConfigEventContent {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
blocked_servers: vec!["*".to_owned()],
|
blocked_servers: vec!["*".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:foo.com:8080")), FilterLevel::Block);
|
assert_eq!(event.user_filter_level(user_id("@alice:foo.com:8080")), FilterLevel::Block);
|
||||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_goodguys() {
|
fn only_goodguys() {
|
||||||
let event = InvitePermissionConfigEventContent {
|
let event = InvitePermissionConfigEventContent {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||||
blocked_servers: vec!["*".to_owned()],
|
blocked_servers: vec!["*".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org:8080")), FilterLevel::Allow);
|
assert_eq!(
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
event.user_filter_level(user_id("@alice:goodguys.org:8080")),
|
||||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
FilterLevel::Allow
|
||||||
}
|
);
|
||||||
|
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||||
|
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn exclude_badguys() {
|
fn exclude_badguys() {
|
||||||
let event = InvitePermissionConfigEventContent {
|
let event = InvitePermissionConfigEventContent {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
blocked_servers: vec!["badguys.org".to_owned()],
|
blocked_servers: vec!["badguys.org".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org:8080")), FilterLevel::Block);
|
assert_eq!(
|
||||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
event.user_filter_level(user_id("@kevin:badguys.org:8080")),
|
||||||
}
|
FilterLevel::Block
|
||||||
|
);
|
||||||
|
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_goodguys_except_for_kevin() {
|
fn only_goodguys_except_for_kevin() {
|
||||||
let event = InvitePermissionConfigEventContent {
|
let event = InvitePermissionConfigEventContent {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
blocked_users: vec!["@kevin:goodguys.org".to_owned()],
|
blocked_users: vec!["@kevin:goodguys.org".to_owned()],
|
||||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||||
blocked_servers: vec!["*".to_owned()],
|
blocked_servers: vec!["*".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||||
assert_eq!(event.user_filter_level(user_id("@kevin:goodguys.org")), FilterLevel::Block);
|
assert_eq!(event.user_filter_level(user_id("@kevin:goodguys.org")), FilterLevel::Block);
|
||||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_badguys_except_for_alice() {
|
fn no_badguys_except_for_alice() {
|
||||||
let event = InvitePermissionConfigEventContent {
|
let event = InvitePermissionConfigEventContent {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
allowed_users: vec!["@alice:badguys.org".to_owned()],
|
allowed_users: vec!["@alice:badguys.org".to_owned()],
|
||||||
blocked_servers: vec!["badguys.org".to_owned()],
|
blocked_servers: vec!["badguys.org".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:badguys.org")), FilterLevel::Allow);
|
assert_eq!(event.user_filter_level(user_id("@alice:badguys.org")), FilterLevel::Allow);
|
||||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Allow);
|
||||||
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
assert_eq!(event.user_filter_level(user_id("@kevin:badguys.org")), FilterLevel::Block);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn only_goodguys_and_ignore_reallybadguys() {
|
fn only_goodguys_and_ignore_reallybadguys() {
|
||||||
let event = InvitePermissionConfigEventContent {
|
let event = InvitePermissionConfigEventContent {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
allowed_servers: vec!["goodguys.org".to_owned()],
|
allowed_servers: vec!["goodguys.org".to_owned()],
|
||||||
ignored_servers: vec!["reallybadguys.org".to_owned()],
|
ignored_servers: vec!["reallybadguys.org".to_owned()],
|
||||||
blocked_servers: vec!["*".to_owned()],
|
blocked_servers: vec!["*".to_owned()],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org:8080")), FilterLevel::Allow);
|
assert_eq!(
|
||||||
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
event.user_filter_level(user_id("@alice:goodguys.org:8080")),
|
||||||
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
FilterLevel::Allow
|
||||||
assert_eq!(event.user_filter_level(user_id("@kevin:reallybadguys.org")), FilterLevel::Ignore);
|
);
|
||||||
}
|
assert_eq!(event.user_filter_level(user_id("@alice:goodguys.org")), FilterLevel::Allow);
|
||||||
}
|
assert_eq!(event.user_filter_level(user_id("@bob:bar.com")), FilterLevel::Block);
|
||||||
|
assert_eq!(
|
||||||
|
event.user_filter_level(user_id("@kevin:reallybadguys.org")),
|
||||||
|
FilterLevel::Ignore
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,53 +7,53 @@
|
|||||||
//! - https://mau.dev/maunium/synapse/-/blob/52741d3/synapse/handlers/event_auth.py#L280-292
|
//! - https://mau.dev/maunium/synapse/-/blob/52741d3/synapse/handlers/event_auth.py#L280-292
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
OwnedRoomId, OwnedUserId,
|
||||||
};
|
api::{auth_scheme::AppserviceToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: AppserviceToken,
|
authentication: AppserviceToken,
|
||||||
history: {
|
history: {
|
||||||
1.0 => "/_meowlnir/antispam/{management_room_id}/accept_make_join",
|
1.0 => "/_meowlnir/antispam/{management_room_id}/accept_make_join",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the `accept_make_join` callback.
|
/// Request type for the `accept_make_join` callback.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The relevant management room
|
/// The relevant management room
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub management_room_id: OwnedRoomId,
|
pub management_room_id: OwnedRoomId,
|
||||||
/// The user trying to join a room
|
/// The user trying to join a room
|
||||||
pub user: OwnedUserId,
|
pub user: OwnedUserId,
|
||||||
/// The room the user is trying to join
|
/// The room the user is trying to join
|
||||||
pub room: OwnedRoomId,
|
pub room: OwnedRoomId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `accept_make_join` callback.
|
/// Response type for the `accept_make_join` callback.
|
||||||
#[response]
|
#[response]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Response;
|
pub struct Response;
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new empty `Request`.
|
/// Creates a new empty `Request`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
management_room_id: OwnedRoomId,
|
management_room_id: OwnedRoomId,
|
||||||
user: OwnedUserId,
|
user: OwnedUserId,
|
||||||
room: OwnedRoomId,
|
room: OwnedRoomId,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { management_room_id, user, room }
|
Self { management_room_id, user, room }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new empty `Response`.
|
/// Creates a new empty `Response`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
pub mod user_may_invite;
|
pub mod user_may_invite;
|
||||||
pub mod user_may_join_room;
|
pub mod user_may_join_room;
|
||||||
|
|
||||||
pub mod accept_make_join;
|
pub mod accept_make_join;
|
||||||
|
|||||||
@@ -1,58 +1,64 @@
|
|||||||
//! `POST /_meowlnir/antispam/*/user_may_invite`
|
//! `POST /_meowlnir/antispam/*/user_may_invite`
|
||||||
//!
|
//!
|
||||||
//! Checks that a user may invite the given user to the given room via Meowlnir anti-spam
|
//! Checks that a user may invite the given user to the given room via Meowlnir
|
||||||
|
//! anti-spam
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
OwnedRoomId, OwnedUserId,
|
||||||
};
|
api::{auth_scheme::AppserviceToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: AppserviceToken,
|
authentication: AppserviceToken,
|
||||||
history: {
|
history: {
|
||||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_invite",
|
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_invite",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the `user_may_invite` callback.
|
/// Request type for the `user_may_invite` callback.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The relevant management room
|
/// The relevant management room
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub management_room_id: OwnedRoomId,
|
pub management_room_id: OwnedRoomId,
|
||||||
/// The user sending the invite
|
/// The user sending the invite
|
||||||
pub inviter: OwnedUserId,
|
pub inviter: OwnedUserId,
|
||||||
/// The user being invited
|
/// The user being invited
|
||||||
pub invitee: OwnedUserId,
|
pub invitee: OwnedUserId,
|
||||||
/// The room the invitee is being invited to
|
/// The room the invitee is being invited to
|
||||||
pub room_id: OwnedRoomId,
|
pub room_id: OwnedRoomId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `user_may_invite` callback.
|
/// Response type for the `user_may_invite` callback.
|
||||||
#[response]
|
#[response]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Response;
|
pub struct Response;
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new empty `Request`.
|
/// Creates a new empty `Request`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
management_room_id: OwnedRoomId,
|
management_room_id: OwnedRoomId,
|
||||||
inviter: OwnedUserId,
|
inviter: OwnedUserId,
|
||||||
invitee: OwnedUserId,
|
invitee: OwnedUserId,
|
||||||
room_id: OwnedRoomId,
|
room_id: OwnedRoomId,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { management_room_id, inviter, invitee, room_id }
|
Self {
|
||||||
}
|
management_room_id,
|
||||||
}
|
inviter,
|
||||||
|
invitee,
|
||||||
|
room_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new empty `Response`.
|
/// Creates a new empty `Response`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,56 +3,61 @@
|
|||||||
//! Endpoint to track invite joins via Meowlnir anti-spam
|
//! Endpoint to track invite joins via Meowlnir anti-spam
|
||||||
|
|
||||||
pub mod v1 {
|
pub mod v1 {
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, request, response}, metadata
|
OwnedRoomId, OwnedUserId,
|
||||||
};
|
api::{auth_scheme::AppserviceToken, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: AppserviceToken,
|
authentication: AppserviceToken,
|
||||||
history: {
|
history: {
|
||||||
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_join_room",
|
1.0 => "/_meowlnir/antispam/{management_room_id}/user_may_join_room",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the `user_may_join_room` callback.
|
/// Request type for the `user_may_join_room` callback.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The relevant management room
|
/// The relevant management room
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub management_room_id: OwnedRoomId,
|
pub management_room_id: OwnedRoomId,
|
||||||
/// The user trying to join a room
|
/// The user trying to join a room
|
||||||
pub user: OwnedUserId,
|
pub user: OwnedUserId,
|
||||||
/// The room the user is trying to join
|
/// The room the user is trying to join
|
||||||
pub room: OwnedRoomId,
|
pub room: OwnedRoomId,
|
||||||
/// Whether the user was invited to this room
|
/// Whether the user was invited to this room
|
||||||
pub is_invited: bool,
|
pub is_invited: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `user_may_join_room` callback.
|
/// Response type for the `user_may_join_room` callback.
|
||||||
#[response]
|
#[response]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Response;
|
pub struct Response;
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new empty `Request`.
|
/// Creates a new empty `Request`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
management_room_id: OwnedRoomId,
|
management_room_id: OwnedRoomId,
|
||||||
user: OwnedUserId,
|
user: OwnedUserId,
|
||||||
room: OwnedRoomId,
|
room: OwnedRoomId,
|
||||||
is_invited: bool,
|
is_invited: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { management_room_id, user, room, is_invited }
|
Self {
|
||||||
}
|
management_room_id,
|
||||||
}
|
user,
|
||||||
|
room,
|
||||||
|
is_invited,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new empty `Response`.
|
/// Creates a new empty `Response`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod draupnir_antispam;
|
pub mod draupnir_antispam;
|
||||||
|
pub mod invite_permission_config;
|
||||||
pub mod meowlnir_antispam;
|
pub mod meowlnir_antispam;
|
||||||
pub mod policy;
|
pub mod policy;
|
||||||
pub mod invite_permission_config;
|
|
||||||
@@ -2,99 +2,90 @@
|
|||||||
//!
|
//!
|
||||||
//! [`org.matrix.msc4284.policy`]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
//! [`org.matrix.msc4284.policy`]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||||
|
|
||||||
use ruma::exports::ruma_macros::EventContent;
|
use ruma::{events::EmptyStateKey, exports::ruma_macros::EventContent};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use ruma::events::EmptyStateKey;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Default)]
|
#[derive(Clone, Debug, Deserialize, Serialize, EventContent, Default)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[ruma_event(type = "org.matrix.msc4284.policy", kind = State, state_key_type = EmptyStateKey)]
|
#[ruma_event(type = "org.matrix.msc4284.policy", kind = State, state_key_type = EmptyStateKey)]
|
||||||
pub struct RoomPolicyEventContent {
|
pub struct RoomPolicyEventContent {
|
||||||
/// The server name of the room's policy server.
|
/// The server name of the room's policy server.
|
||||||
///
|
///
|
||||||
/// If the value is empty or unreachable, the policy server should be ignored.
|
/// If the value is empty or unreachable, the policy server should be
|
||||||
pub via: Option<String>,
|
/// ignored.
|
||||||
/// The public key this policy server will sign with.
|
pub via: Option<String>,
|
||||||
pub public_key: Option<String>
|
/// The public key this policy server will sign with.
|
||||||
|
pub public_key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RoomPolicyEventContent {
|
impl RoomPolicyEventContent {
|
||||||
/// Create an empty `RoomPolicyEventContent`.
|
/// Create an empty `RoomPolicyEventContent`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
pub struct PolicyServerResponseContent {
|
pub struct PolicyServerResponseContent {
|
||||||
/// The policy server's verdict. Either `ok` or `spam`.
|
/// The policy server's verdict. Either `ok` or `spam`.
|
||||||
pub recommendation: String,
|
pub recommendation: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PolicyServerResponseContent {
|
impl PolicyServerResponseContent {
|
||||||
/// Create a new `PolicyServerResponseContent` with the given recommendation.
|
/// Create a new `PolicyServerResponseContent` with the given
|
||||||
#[must_use]
|
/// recommendation.
|
||||||
pub fn new(recommendation: String) -> Self {
|
#[must_use]
|
||||||
Self { recommendation }
|
pub fn new(recommendation: String) -> Self { Self { recommendation } }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<String> for PolicyServerResponseContent {
|
impl From<String> for PolicyServerResponseContent {
|
||||||
fn from(recommendation: String) -> Self {
|
fn from(recommendation: String) -> Self { Self::new(recommendation) }
|
||||||
Self::new(recommendation)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
use ruma::events::OriginalStateEvent;
|
||||||
|
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
|
||||||
|
|
||||||
use super::RoomPolicyEventContent;
|
use super::RoomPolicyEventContent;
|
||||||
use ruma::events::OriginalStateEvent;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialization() {
|
fn serialization() {
|
||||||
let content = RoomPolicyEventContent {
|
let content = RoomPolicyEventContent {
|
||||||
via: Some("example.com".to_owned()),
|
via: Some("example.com".to_owned()),
|
||||||
public_key: Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned())
|
public_key: Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let actual = to_json_value(content).unwrap();
|
let actual = to_json_value(content).unwrap();
|
||||||
let expected = json!({
|
let expected = json!({
|
||||||
"via": "example.com",
|
"via": "example.com",
|
||||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserialization() {
|
fn deserialization() {
|
||||||
let json_data = json!({
|
let json_data = json!({
|
||||||
"content": {
|
"content": {
|
||||||
"via": "example.com",
|
"via": "example.com",
|
||||||
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
"public_key": "6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s"
|
||||||
},
|
},
|
||||||
"event_id": "123:example.com",
|
"event_id": "123:example.com",
|
||||||
"origin_server_ts": 1,
|
"origin_server_ts": 1,
|
||||||
"room_id": "!123456:example.com",
|
"room_id": "!123456:example.com",
|
||||||
"sender": "@carl:example.com",
|
"sender": "@carl:example.com",
|
||||||
"state_key": "",
|
"state_key": "",
|
||||||
"type": "org.matrix.msc4284.policy"
|
"type": "org.matrix.msc4284.policy"
|
||||||
});
|
});
|
||||||
|
|
||||||
let content = from_json_value::<OriginalStateEvent<RoomPolicyEventContent>>(json_data)
|
let content = from_json_value::<OriginalStateEvent<RoomPolicyEventContent>>(json_data)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.content;
|
.content;
|
||||||
assert_eq!(
|
assert_eq!(content.via, Some("example.com".to_owned()));
|
||||||
content.via,
|
assert_eq!(
|
||||||
Some("example.com".to_owned())
|
content.public_key,
|
||||||
);
|
Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned())
|
||||||
assert_eq!(
|
);
|
||||||
content.public_key,
|
}
|
||||||
Some("6yhHGKhCiXTSEN2ksjV7kX_N6rBQZ3Xb-M7LlC6NS-s".to_owned())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
|
pub mod event;
|
||||||
pub mod policy_check;
|
pub mod policy_check;
|
||||||
pub mod policy_sign;
|
pub mod policy_sign;
|
||||||
pub mod report_content;
|
pub mod report_content;
|
||||||
pub mod event;
|
|
||||||
@@ -4,57 +4,56 @@
|
|||||||
//! This is now a fallback behaviour that will be removed later.
|
//! This is now a fallback behaviour that will be removed later.
|
||||||
|
|
||||||
pub mod unstable {
|
pub mod unstable {
|
||||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||||
//!
|
//!
|
||||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedEventId, api::{federation::authentication::ServerSignatures, request, response}, metadata
|
OwnedEventId,
|
||||||
};
|
api::{federation::authentication::ServerSignatures, request, response},
|
||||||
use serde_json::value::RawValue as RawJsonValue;
|
metadata,
|
||||||
|
};
|
||||||
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: ServerSignatures,
|
authentication: ServerSignatures,
|
||||||
history: {
|
history: {
|
||||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/event/{event_id}/check",
|
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/event/{event_id}/check",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `check` endpoint.
|
/// Response type for the `check` endpoint.
|
||||||
#[response]
|
#[response]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
/// Either `ok` or `spam`, indicating the policy server's recommendation.
|
/// Either `ok` or `spam`, indicating the policy server's
|
||||||
pub recommendation: String,
|
/// recommendation.
|
||||||
}
|
pub recommendation: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new `Response` with the given recommendation.
|
/// Creates a new `Response` with the given recommendation.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(recommendation: String) -> Self {
|
pub fn new(recommendation: String) -> Self { Self { recommendation } }
|
||||||
Self { recommendation }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request type for the `check` endpoint.
|
/// Request type for the `check` endpoint.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The event ID to check.
|
/// The event ID to check.
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub event_id: OwnedEventId,
|
pub event_id: OwnedEventId,
|
||||||
|
|
||||||
/// The PDU body (optional)
|
/// The PDU body (optional)
|
||||||
#[ruma_api(body)]
|
#[ruma_api(body)]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub pdu: Option<Box<RawJsonValue>>,
|
pub pdu: Option<Box<RawJsonValue>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new `Request` with the given event ID.
|
/// Creates a new `Request` with the given event ID.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(event_id: OwnedEventId) -> Self {
|
pub fn new(event_id: OwnedEventId) -> Self { Self { event_id, pdu: None } }
|
||||||
Self { event_id, pdu: None }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,53 +3,53 @@
|
|||||||
//! Asks a policy server to sign our event
|
//! Asks a policy server to sign our event
|
||||||
|
|
||||||
pub mod unstable {
|
pub mod unstable {
|
||||||
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
//! `/policy/unstable/org.matrix.msc4284` ([spec])
|
||||||
//!
|
//!
|
||||||
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
//! [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/4284
|
||||||
use ruma::{
|
use ruma::{
|
||||||
ServerSignatures,
|
ServerSignatures,
|
||||||
api::{federation::authentication::ServerSignatures as ServerSignaturesAuth, request, response}, metadata
|
api::{
|
||||||
};
|
federation::authentication::ServerSignatures as ServerSignaturesAuth, request,
|
||||||
use serde_json::value::RawValue as RawJsonValue;
|
response,
|
||||||
|
},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: ServerSignaturesAuth,
|
authentication: ServerSignaturesAuth,
|
||||||
history: {
|
history: {
|
||||||
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/sign",
|
unstable => "/_matrix/policy/unstable/org.matrix.msc4284/sign",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `sign` endpoint.
|
/// Response type for the `sign` endpoint.
|
||||||
#[response]
|
#[response]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
/// The signatures returned from the policy server (if provided)
|
/// The signatures returned from the policy server (if provided)
|
||||||
#[ruma_api(body)]
|
#[ruma_api(body)]
|
||||||
pub signatures: Option<ServerSignatures>
|
pub signatures: Option<ServerSignatures>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new `Response` with the given recommendation.
|
/// Creates a new `Response` with the given recommendation.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(signatures: Option<ServerSignatures>) -> Self {
|
pub fn new(signatures: Option<ServerSignatures>) -> Self { Self { signatures } }
|
||||||
Self { signatures }
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Request type for the `sign` endpoint.
|
/// Request type for the `sign` endpoint.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The PDU body (in canonical JSON)
|
/// The PDU body (in canonical JSON)
|
||||||
#[ruma_api(body)]
|
#[ruma_api(body)]
|
||||||
pub pdu: Box<RawJsonValue>,
|
pub pdu: Box<RawJsonValue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a new `Request` with the given event JSON
|
/// Creates a new `Request` with the given event JSON
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(pdu: Box<RawJsonValue>) -> Self {
|
pub fn new(pdu: Box<RawJsonValue>) -> Self { Self { pdu } }
|
||||||
Self { pdu }
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,56 +3,56 @@
|
|||||||
//! Send a request to report an event originating from another server.
|
//! Send a request to report an event originating from another server.
|
||||||
|
|
||||||
pub mod msc3843 {
|
pub mod msc3843 {
|
||||||
//! `MSC3843` ([MSC])
|
//! `MSC3843` ([MSC])
|
||||||
//!
|
//!
|
||||||
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3843
|
//! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/3843
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedEventId, OwnedRoomId, api::{federation::authentication::ServerSignatures, request, response}, metadata
|
OwnedEventId, OwnedRoomId,
|
||||||
};
|
api::{federation::authentication::ServerSignatures, request, response},
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
|
||||||
metadata! {
|
metadata! {
|
||||||
method: POST,
|
method: POST,
|
||||||
rate_limited: false,
|
rate_limited: false,
|
||||||
authentication: ServerSignatures,
|
authentication: ServerSignatures,
|
||||||
history: {
|
history: {
|
||||||
unstable => "/_matrix/federation/unstable/org.matrix.msc3843/rooms/{room_id}/report/{event_id}",
|
unstable => "/_matrix/federation/unstable/org.matrix.msc3843/rooms/{room_id}/report/{event_id}",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request type for the `report_content` endpoint.
|
/// Request type for the `report_content` endpoint.
|
||||||
#[request]
|
#[request]
|
||||||
pub struct Request {
|
pub struct Request {
|
||||||
/// The room ID that the reported event was sent in.
|
/// The room ID that the reported event was sent in.
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub room_id: OwnedRoomId,
|
pub room_id: OwnedRoomId,
|
||||||
|
|
||||||
/// The event being reported.
|
/// The event being reported.
|
||||||
#[ruma_api(path)]
|
#[ruma_api(path)]
|
||||||
pub event_id: OwnedEventId,
|
pub event_id: OwnedEventId,
|
||||||
|
|
||||||
/// The reason that the event is being reported.
|
/// The reason that the event is being reported.
|
||||||
pub reason: String,
|
pub reason: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Response type for the `report_content` endpoint.
|
/// Response type for the `report_content` endpoint.
|
||||||
#[response]
|
#[response]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Response;
|
pub struct Response;
|
||||||
|
|
||||||
impl Request {
|
impl Request {
|
||||||
/// Creates a `Request` with the given room ID, event ID and reason.
|
/// Creates a `Request` with the given room ID, event ID and reason.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(room_id: OwnedRoomId, event_id: OwnedEventId, reason: String) -> Self {
|
pub fn new(room_id: OwnedRoomId, event_id: OwnedEventId, reason: String) -> Self {
|
||||||
Self { room_id, event_id, reason }
|
Self { room_id, event_id, reason }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Response {
|
impl Response {
|
||||||
/// Creates a new empty `Response`.
|
/// Creates a new empty `Response`.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self { Self::default() }
|
||||||
Self::default()
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,7 @@ nonzero_ext.workspace = true
|
|||||||
rand.workspace = true
|
rand.workspace = true
|
||||||
regex.workspace = true
|
regex.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
|
assign.workspace = true
|
||||||
ruma.workspace = true
|
ruma.workspace = true
|
||||||
ruminuwuity.workspace = true
|
ruminuwuity.workspace = true
|
||||||
rustyline-async.workspace = true
|
rustyline-async.workspace = true
|
||||||
|
|||||||
@@ -7,10 +7,12 @@ use conduwuit::{
|
|||||||
use database::{Deserialized, Handle, Ignore, Json, Map};
|
use database::{Deserialized, Handle, Ignore, Json, Map};
|
||||||
use futures::{Stream, StreamExt, TryFutureExt};
|
use futures::{Stream, StreamExt, TryFutureExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedUserId, RoomId, UserId, events::{
|
OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||||
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent,
|
events::{
|
||||||
GlobalAccountDataEventType, RoomAccountDataEventType,
|
AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, GlobalAccountDataEventType,
|
||||||
}, serde::Raw
|
RoomAccountDataEventType,
|
||||||
|
},
|
||||||
|
serde::Raw,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
@@ -147,7 +149,9 @@ pub fn changes_since<'a>(
|
|||||||
.stream_from(&first_possible)
|
.stream_from(&first_possible)
|
||||||
.ignore_err()
|
.ignore_err()
|
||||||
.ready_take_while(move |((room_id_, user_id_, count, _), _): &(Key, _)| {
|
.ready_take_while(move |((room_id_, user_id_, count, _), _): &(Key, _)| {
|
||||||
room_id == room_id_.as_deref() && user_id == user_id_ && to.is_none_or(|to| *count <= to)
|
room_id == room_id_.as_deref()
|
||||||
|
&& user_id == user_id_
|
||||||
|
&& to.is_none_or(|to| *count <= to)
|
||||||
})
|
})
|
||||||
.map(move |(_, v)| {
|
.map(move |(_, v)| {
|
||||||
match room_id {
|
match room_id {
|
||||||
|
|||||||
@@ -85,7 +85,8 @@ pub async fn create_admin_room(services: &Services) -> Result {
|
|||||||
// 3. Power levels
|
// 3. Power levels
|
||||||
let users = BTreeMap::from_iter([(server_user.into(), 69420.into())]);
|
let users = BTreeMap::from_iter([(server_user.into(), 69420.into())]);
|
||||||
|
|
||||||
let mut power_levels_content = RoomPowerLevelsEventContent::new(&room_version.rules().unwrap().authorization);
|
let mut power_levels_content =
|
||||||
|
RoomPowerLevelsEventContent::new(&room_version.rules().unwrap().authorization);
|
||||||
power_levels_content.users = users;
|
power_levels_content.users = users;
|
||||||
|
|
||||||
services
|
services
|
||||||
|
|||||||
@@ -30,7 +30,11 @@ use ruma::{
|
|||||||
};
|
};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use crate::{Dep, account_data, globals, media::{MXC_LENGTH, mxc::Mxc}, rooms::{self, state::RoomMutexGuard}};
|
use crate::{
|
||||||
|
Dep, account_data, globals,
|
||||||
|
media::{MXC_LENGTH, mxc::Mxc},
|
||||||
|
rooms::{self, state::RoomMutexGuard},
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
services: Services,
|
services: Services,
|
||||||
@@ -61,7 +65,7 @@ pub struct CommandInput {
|
|||||||
pub command: String,
|
pub command: String,
|
||||||
pub reply_id: Option<OwnedEventId>,
|
pub reply_id: Option<OwnedEventId>,
|
||||||
pub source: InvocationSource,
|
pub source: InvocationSource,
|
||||||
pub sender: Option<Box<UserId>>,
|
pub sender: Option<OwnedUserId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Where a command is being invoked from.
|
/// Where a command is being invoked from.
|
||||||
@@ -205,7 +209,10 @@ impl Service {
|
|||||||
metadata.mimetype = Some("text/markdown".to_owned());
|
metadata.mimetype = Some("text/markdown".to_owned());
|
||||||
metadata.size = Some(UInt::new_saturating(size_u64));
|
metadata.size = Some(UInt::new_saturating(size_u64));
|
||||||
|
|
||||||
let mut content = FileMessageEventContent::plain("Output was too large to send as text.".to_owned(), file);
|
let mut content = FileMessageEventContent::plain(
|
||||||
|
"Output was too large to send as text.".to_owned(),
|
||||||
|
file,
|
||||||
|
);
|
||||||
content.filename = Some("output.md".to_owned());
|
content.filename = Some("output.md".to_owned());
|
||||||
content.info = Some(Box::new(metadata));
|
content.info = Some(Box::new(metadata));
|
||||||
|
|
||||||
@@ -313,7 +320,7 @@ impl Service {
|
|||||||
command: String,
|
command: String,
|
||||||
reply_id: Option<OwnedEventId>,
|
reply_id: Option<OwnedEventId>,
|
||||||
source: InvocationSource,
|
source: InvocationSource,
|
||||||
sender: Box<UserId>,
|
sender: OwnedUserId,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.channel
|
self.channel
|
||||||
.0
|
.0
|
||||||
@@ -448,13 +455,13 @@ impl Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_response(&self, content: RoomMessageEventContent) -> Result<()> {
|
async fn handle_response(&self, content: RoomMessageEventContent) -> Result<()> {
|
||||||
let Some(Relation::Reply { in_reply_to }) = content.relates_to.as_ref() else {
|
let Some(Relation::Reply(reply )) = content.relates_to.as_ref() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let Ok(pdu) = self.services.timeline.get_pdu(&in_reply_to.event_id).await else {
|
let Ok(pdu) = self.services.timeline.get_pdu(&reply.in_reply_to.event_id).await else {
|
||||||
error!(
|
error!(
|
||||||
event_id = ?in_reply_to.event_id,
|
event_id = ?reply.in_reply_to.event_id,
|
||||||
"Missing admin command in_reply_to event"
|
"Missing admin command in_reply_to event"
|
||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ use std::{fmt::Debug, sync::Arc};
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use conduwuit::{Result, config::Antispam, debug};
|
use conduwuit::{Result, config::Antispam, debug};
|
||||||
use ruma::{OwnedRoomId, OwnedUserId, api::{auth_scheme::AppserviceToken, path_builder::VersionHistory}};
|
use ruma::{
|
||||||
|
OwnedRoomId, OwnedUserId,
|
||||||
|
api::{auth_scheme::AppserviceToken, path_builder::VersionHistory},
|
||||||
|
};
|
||||||
use ruminuwuity::{draupnir_antispam, meowlnir_antispam};
|
use ruminuwuity::{draupnir_antispam, meowlnir_antispam};
|
||||||
|
|
||||||
use crate::{client, config, sending, service::Dep};
|
use crate::{client, config, sending, service::Dep};
|
||||||
@@ -38,7 +41,11 @@ impl Service {
|
|||||||
request: T,
|
request: T,
|
||||||
) -> Result<T::IncomingResponse>
|
) -> Result<T::IncomingResponse>
|
||||||
where
|
where
|
||||||
T: ruma::api::OutgoingRequest<Authentication = AppserviceToken, PathBuilder = VersionHistory> + Debug + Send,
|
T: ruma::api::OutgoingRequest<
|
||||||
|
Authentication = AppserviceToken,
|
||||||
|
PathBuilder = VersionHistory,
|
||||||
|
> + Debug
|
||||||
|
+ Send,
|
||||||
{
|
{
|
||||||
sending::antispam::send_antispam_request(
|
sending::antispam::send_antispam_request(
|
||||||
&self.services.client.appservice,
|
&self.services.client.appservice,
|
||||||
|
|||||||
@@ -72,7 +72,9 @@ impl Service {
|
|||||||
None,
|
None,
|
||||||
server_user,
|
server_user,
|
||||||
GlobalAccountDataEventType::PushRules.to_string().into(),
|
GlobalAccountDataEventType::PushRules.to_string().into(),
|
||||||
&serde_json::to_value(&GlobalAccountDataEvent::new(PushRulesEventContent::new(ruleset)))
|
&serde_json::to_value(&GlobalAccountDataEvent::new(PushRulesEventContent::new(
|
||||||
|
ruleset,
|
||||||
|
)))
|
||||||
.expect("to json value always works"),
|
.expect("to json value always works"),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|||||||
@@ -2,13 +2,23 @@ use std::{any::Any, borrow::Cow, fmt::Debug, mem, sync::LazyLock};
|
|||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Result, debug, debug_error, debug_warn, err, implement, trace, utils::response::LimitReadExt, matrix::versions::{unstable_features, versions}, };
|
Err, Error, Result, debug, debug_error, debug_warn, err, implement,
|
||||||
|
matrix::versions::{unstable_features, versions},
|
||||||
|
trace,
|
||||||
|
utils::response::LimitReadExt,
|
||||||
|
};
|
||||||
use ipaddress::IPAddress;
|
use ipaddress::IPAddress;
|
||||||
use reqwest::{Client, Method, Request, Response, Url};
|
use reqwest::{Client, Method, Request, Response, Url};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, CanonicalJsonValue, ServerName, ServerSigningKeyId, api::{
|
CanonicalJsonObject, CanonicalJsonValue, ServerName, ServerSigningKeyId,
|
||||||
EndpointError, IncomingResponse, Metadata, OutgoingRequest, SupportedVersions, auth_scheme::{AuthScheme, NoAuthentication, SendAccessToken}, client::error::Error as RumaError, federation::authentication::{ServerSignatures, ServerSignaturesInput, XMatrix}, path_builder::{PathBuilder, SinglePath, VersionHistory}
|
api::{
|
||||||
}, serde::Base64
|
EndpointError, IncomingResponse, Metadata, OutgoingRequest, SupportedVersions,
|
||||||
|
auth_scheme::{AuthScheme, NoAuthentication, SendAccessToken},
|
||||||
|
error::Error as RumaError,
|
||||||
|
federation::authentication::{ServerSignatures, ServerSignaturesInput, XMatrix},
|
||||||
|
path_builder::{PathBuilder, SinglePath, VersionHistory},
|
||||||
|
},
|
||||||
|
serde::Base64,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{SUPPORTED_VERSIONS, resolver::actual::ActualDest};
|
use crate::{SUPPORTED_VERSIONS, resolver::actual::ActualDest};
|
||||||
@@ -18,7 +28,11 @@ use crate::{SUPPORTED_VERSIONS, resolver::actual::ActualDest};
|
|||||||
#[tracing::instrument(skip_all, name = "request", level = "debug")]
|
#[tracing::instrument(skip_all, name = "request", level = "debug")]
|
||||||
pub async fn execute<'i, T>(&self, dest: &ServerName, request: T) -> Result<T::IncomingResponse>
|
pub async fn execute<'i, T>(&self, dest: &ServerName, request: T) -> Result<T::IncomingResponse>
|
||||||
where
|
where
|
||||||
T: OutgoingRequest::<Authentication = ServerSignatures, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Debug + Send,
|
T: OutgoingRequest<
|
||||||
|
Authentication = ServerSignatures,
|
||||||
|
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||||
|
> + Debug
|
||||||
|
+ Send,
|
||||||
{
|
{
|
||||||
let client = &self.services.client.federation;
|
let client = &self.services.client.federation;
|
||||||
self.execute_signed(client, dest, request).await
|
self.execute_signed(client, dest, request).await
|
||||||
@@ -33,27 +47,46 @@ pub async fn execute_synapse<'i, T>(
|
|||||||
request: T,
|
request: T,
|
||||||
) -> Result<T::IncomingResponse>
|
) -> Result<T::IncomingResponse>
|
||||||
where
|
where
|
||||||
T: OutgoingRequest::<Authentication = ServerSignatures, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Debug + Send,
|
T: OutgoingRequest<
|
||||||
|
Authentication = ServerSignatures,
|
||||||
|
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||||
|
> + Debug
|
||||||
|
+ Send,
|
||||||
{
|
{
|
||||||
let client = &self.services.client.synapse;
|
let client = &self.services.client.synapse;
|
||||||
self.execute_signed(client, dest, request).await
|
self.execute_signed(client, dest, request).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
pub async fn execute_unauthenticated<'i, T>(&self, dest: &ServerName, request: T) -> Result<T::IncomingResponse>
|
pub async fn execute_unauthenticated<'i, T>(
|
||||||
where
|
&self,
|
||||||
T: OutgoingRequest::<Authentication = NoAuthentication, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Debug + Send,
|
dest: &ServerName,
|
||||||
|
request: T,
|
||||||
|
) -> Result<T::IncomingResponse>
|
||||||
|
where
|
||||||
|
T: OutgoingRequest<
|
||||||
|
Authentication = NoAuthentication,
|
||||||
|
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||||
|
> + Debug
|
||||||
|
+ Send,
|
||||||
{
|
{
|
||||||
let client = &self.services.client.federation;
|
let client = &self.services.client.federation;
|
||||||
let authentication = SendAccessToken::None;
|
|
||||||
|
self.execute_on(client, dest, request, ()).await
|
||||||
self.execute_on(client, dest, request, authentication).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
pub async fn execute_signed<'i, T>(&self, client: &Client, dest: &ServerName, request: T) -> Result<T::IncomingResponse>
|
pub async fn execute_signed<'i, T>(
|
||||||
|
&self,
|
||||||
|
client: &Client,
|
||||||
|
dest: &ServerName,
|
||||||
|
request: T,
|
||||||
|
) -> Result<T::IncomingResponse>
|
||||||
where
|
where
|
||||||
T: OutgoingRequest::<Authentication = ServerSignatures, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Send,
|
T: OutgoingRequest<
|
||||||
|
Authentication = ServerSignatures,
|
||||||
|
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||||
|
> + Send,
|
||||||
{
|
{
|
||||||
let authentication = ServerSignaturesInput::new(
|
let authentication = ServerSignaturesInput::new(
|
||||||
self.services.server.name.clone(),
|
self.services.server.name.clone(),
|
||||||
@@ -65,11 +98,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
#[tracing::instrument(
|
#[tracing::instrument(name = "fed", level = "info", skip(self, client, request, authentication))]
|
||||||
name = "fed",
|
|
||||||
level = "info",
|
|
||||||
skip(self, client, request, authentication),
|
|
||||||
)]
|
|
||||||
pub async fn execute_on<'i, T, PathBuilderInput>(
|
pub async fn execute_on<'i, T, PathBuilderInput>(
|
||||||
&self,
|
&self,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
@@ -78,8 +107,8 @@ pub async fn execute_on<'i, T, PathBuilderInput>(
|
|||||||
authentication: <T::Authentication as AuthScheme>::Input<'_>,
|
authentication: <T::Authentication as AuthScheme>::Input<'_>,
|
||||||
) -> Result<T::IncomingResponse>
|
) -> Result<T::IncomingResponse>
|
||||||
where
|
where
|
||||||
T: OutgoingRequest::<PathBuilder: PathBuilder<Input<'i> = PathBuilderInput>> + Send,
|
T: OutgoingRequest<PathBuilder: PathBuilder<Input<'i> = PathBuilderInput>> + Send,
|
||||||
PathBuilderInput: FederationPathBuilderInput
|
PathBuilderInput: FederationPathBuilderInput,
|
||||||
{
|
{
|
||||||
if !self.services.server.config.allow_federation {
|
if !self.services.server.config.allow_federation {
|
||||||
return Err!(Config("allow_federation", "Federation is disabled."));
|
return Err!(Config("allow_federation", "Federation is disabled."));
|
||||||
@@ -90,14 +119,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let actual = self.services.resolver.get_actual_dest(dest).await?;
|
let actual = self.services.resolver.get_actual_dest(dest).await?;
|
||||||
|
|
||||||
let request = Request::try_from(
|
let request = Request::try_from(request.try_into_http_request::<Vec<u8>>(
|
||||||
request.try_into_http_request::<Vec<u8>>(
|
actual.string().as_str(),
|
||||||
actual.string().as_str(),
|
authentication,
|
||||||
authentication,
|
PathBuilderInput::create(),
|
||||||
PathBuilderInput::create(),
|
)?)?;
|
||||||
)?
|
|
||||||
)?;
|
|
||||||
self.validate_url(request.url())?;
|
self.validate_url(request.url())?;
|
||||||
self.services.server.check_running()?;
|
self.services.server.check_running()?;
|
||||||
|
|
||||||
@@ -248,17 +275,23 @@ fn handle_error(
|
|||||||
Err(e.into())
|
Err(e.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait for the input types of acceptable path builders for outgoing federation requests.
|
/// A trait for the input types of acceptable path builders for outgoing
|
||||||
///
|
/// federation requests.
|
||||||
/// Ruma uses Rust's type system to encode the versioning scheme of endpoints in the Matrix spec.
|
///
|
||||||
/// Every endpoint has a `PathBuilder` associated type, which has an `Input` associated type.
|
/// Ruma uses Rust's type system to encode the versioning scheme of endpoints in
|
||||||
/// Endpoints with multiple versions have `VersionHistory` as their `PathBuilder`, which has `SupportedVersions`
|
/// the Matrix spec. Every endpoint has a `PathBuilder` associated type, which
|
||||||
/// as its `Input` type. Endpoints with no version have `SinglePath` as their `PathBuilder`, which has `()` as its `Input` type.
|
/// has an `Input` associated type. Endpoints with multiple versions have
|
||||||
/// Both `SupportedVersions` and `()` can be created out of thin air using static data (or no data at all). This property
|
/// `VersionHistory` as their `PathBuilder`, which has `SupportedVersions`
|
||||||
/// is what the `FederationPathBuilderInput` trait represents.
|
/// as its `Input` type. Endpoints with no version have `SinglePath` as their
|
||||||
///
|
/// `PathBuilder`, which has `()` as its `Input` type. Both `SupportedVersions`
|
||||||
/// This trait allows the federation sender service's functions to accept requests for either versioned or unversioned endpoints,
|
/// and `()` can be created out of thin air using static data (or no data at
|
||||||
/// by requiring that the `Input` of the `PathBuilder` of the endpoint implements `FederationPathBuilderInput`.
|
/// all). This property is what the `FederationPathBuilderInput` trait
|
||||||
|
/// represents.
|
||||||
|
///
|
||||||
|
/// This trait allows the federation sender service's functions to accept
|
||||||
|
/// requests for either versioned or unversioned endpoints, by requiring that
|
||||||
|
/// the `Input` of the `PathBuilder` of the endpoint implements
|
||||||
|
/// `FederationPathBuilderInput`.
|
||||||
pub(crate) trait FederationPathBuilderInput {
|
pub(crate) trait FederationPathBuilderInput {
|
||||||
fn create() -> Self;
|
fn create() -> Self;
|
||||||
}
|
}
|
||||||
@@ -268,7 +301,5 @@ impl FederationPathBuilderInput for () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FederationPathBuilderInput for Cow<'_, SupportedVersions> {
|
impl FederationPathBuilderInput for Cow<'_, SupportedVersions> {
|
||||||
fn create() -> Self {
|
fn create() -> Self { Cow::Borrowed(&SUPPORTED_VERSIONS) }
|
||||||
Cow::Borrowed(&SUPPORTED_VERSIONS)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
mod execute;
|
mod execute;
|
||||||
pub(crate) use execute::FederationPathBuilderInput;
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use conduwuit::{Result, Server};
|
use conduwuit::{Result, Server};
|
||||||
|
pub(crate) use execute::FederationPathBuilderInput;
|
||||||
|
|
||||||
use crate::{Dep, client, moderation, resolver, server_keys};
|
use crate::{Dep, client, moderation, resolver, server_keys};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ use conduwuit::{
|
|||||||
use database::{Deserialized, Ignore, Interfix, Json, Map};
|
use database::{Deserialized, Ignore, Interfix, Json, Map};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedUserId, RoomId, UserId, api::client::backup::{BackupAlgorithm, KeyBackupData, RoomKeyBackup}, serde::Raw
|
OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||||
|
api::client::backup::{BackupAlgorithm, KeyBackupData, RoomKeyBackup},
|
||||||
|
serde::Raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Dep, globals};
|
use crate::{Dep, globals};
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ use database::{Database, Interfix, Map};
|
|||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use ruma::{OwnedMxcUri, OwnedUserId, UserId, http_headers::ContentDisposition};
|
use ruma::{OwnedMxcUri, OwnedUserId, UserId, http_headers::ContentDisposition};
|
||||||
|
|
||||||
use crate::media::mxc::Mxc;
|
|
||||||
|
|
||||||
use super::{preview::UrlPreviewData, thumbnail::Dim};
|
use super::{preview::UrlPreviewData, thumbnail::Dim};
|
||||||
|
use crate::media::mxc::Mxc;
|
||||||
|
|
||||||
pub(crate) struct Data {
|
pub(crate) struct Data {
|
||||||
mediaid_file: Arc<Map>,
|
mediaid_file: Arc<Map>,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
pub mod blurhash;
|
pub mod blurhash;
|
||||||
pub mod mxc;
|
|
||||||
mod data;
|
mod data;
|
||||||
pub(super) mod migrations;
|
pub(super) mod migrations;
|
||||||
|
pub mod mxc;
|
||||||
mod preview;
|
mod preview;
|
||||||
mod remote;
|
mod remote;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|||||||
+26
-26
@@ -6,49 +6,49 @@ use serde::{Serialize, Serializer};
|
|||||||
/// A structured, valid MXC URI
|
/// A structured, valid MXC URI
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Mxc<'a> {
|
pub struct Mxc<'a> {
|
||||||
/// ServerName part of the MXC URI
|
/// ServerName part of the MXC URI
|
||||||
pub server_name: &'a ServerName,
|
pub server_name: &'a ServerName,
|
||||||
|
|
||||||
/// MediaId part of the MXC URI
|
/// MediaId part of the MXC URI
|
||||||
pub media_id: &'a str,
|
pub media_id: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Mxc<'_> {
|
impl fmt::Display for Mxc<'_> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "mxc://{}/{}", self.server_name, self.media_id)
|
write!(f, "mxc://{}/{}", self.server_name, self.media_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a MxcUri> for Mxc<'a> {
|
impl<'a> TryFrom<&'a MxcUri> for Mxc<'a> {
|
||||||
type Error = MxcUriError;
|
type Error = MxcUriError;
|
||||||
|
|
||||||
fn try_from(s: &'a MxcUri) -> Result<Self, Self::Error> {
|
fn try_from(s: &'a MxcUri) -> Result<Self, Self::Error> {
|
||||||
let (server_name, media_id) = s.parts()?;
|
let (server_name, media_id) = s.parts()?;
|
||||||
|
|
||||||
Ok(Self { server_name, media_id })
|
Ok(Self { server_name, media_id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a str> for Mxc<'a> {
|
impl<'a> TryFrom<&'a str> for Mxc<'a> {
|
||||||
type Error = MxcUriError;
|
type Error = MxcUriError;
|
||||||
|
|
||||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||||
let s: &MxcUri = s.into();
|
let s: &MxcUri = s.into();
|
||||||
s.try_into()
|
s.try_into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a OwnedMxcUri> for Mxc<'a> {
|
impl<'a> TryFrom<&'a OwnedMxcUri> for Mxc<'a> {
|
||||||
type Error = MxcUriError;
|
type Error = MxcUriError;
|
||||||
|
|
||||||
fn try_from(s: &'a OwnedMxcUri) -> Result<Self, Self::Error> {
|
fn try_from(s: &'a OwnedMxcUri) -> Result<Self, Self::Error> {
|
||||||
let s: &MxcUri = s.as_ref();
|
let s: &MxcUri = s.as_ref();
|
||||||
s.try_into()
|
s.try_into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Mxc<'_> {
|
impl Serialize for Mxc<'_> {
|
||||||
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
|
||||||
s.serialize_str(self.to_string().as_str())
|
s.serialize_str(self.to_string().as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-19
@@ -6,17 +6,23 @@ use conduwuit::{
|
|||||||
};
|
};
|
||||||
use http::header::{CONTENT_DISPOSITION, CONTENT_TYPE, HeaderValue};
|
use http::header::{CONTENT_DISPOSITION, CONTENT_TYPE, HeaderValue};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
ServerName, UserId, api::{
|
ServerName, UserId,
|
||||||
Metadata, OutgoingRequest, auth_scheme::NoAuthentication, client::{
|
api::{
|
||||||
error::ErrorKind::{NotFound, Unrecognized},
|
Metadata, OutgoingRequest,
|
||||||
media,
|
auth_scheme::{NoAccessToken, NoAuthentication},
|
||||||
}, federation::{self, authenticated_media::{Content, FileOrLocation}, authentication::ServerSignatures}, path_builder::PathBuilder
|
client::media,
|
||||||
}
|
error::ErrorKind::{NotFound, Unrecognized},
|
||||||
|
federation::{
|
||||||
|
self,
|
||||||
|
authenticated_media::{Content, FileOrLocation},
|
||||||
|
authentication::ServerSignatures,
|
||||||
|
},
|
||||||
|
path_builder::PathBuilder,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{federation::FederationPathBuilderInput, media::mxc::Mxc};
|
|
||||||
|
|
||||||
use super::{Dim, FileMeta};
|
use super::{Dim, FileMeta};
|
||||||
|
use crate::{federation::FederationPathBuilderInput, media::mxc::Mxc};
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
pub async fn fetch_remote_thumbnail(
|
pub async fn fetch_remote_thumbnail(
|
||||||
@@ -134,7 +140,12 @@ async fn fetch_thumbnail_unauthenticated(
|
|||||||
) -> Result<FileMeta> {
|
) -> Result<FileMeta> {
|
||||||
use media::get_content_thumbnail::v3::{Request, Response};
|
use media::get_content_thumbnail::v3::{Request, Response};
|
||||||
|
|
||||||
let mut request = Request::new(mxc.media_id.into(), mxc.server_name.into(), dim.width.into(), dim.height.into());
|
let mut request = Request::new(
|
||||||
|
mxc.media_id.into(),
|
||||||
|
mxc.server_name.into(),
|
||||||
|
dim.width.into(),
|
||||||
|
dim.height.into(),
|
||||||
|
);
|
||||||
request.allow_redirect = true;
|
request.allow_redirect = true;
|
||||||
request.allow_remote = true;
|
request.allow_remote = true;
|
||||||
request.animated = Some(true);
|
request.animated = Some(true);
|
||||||
@@ -143,7 +154,9 @@ async fn fetch_thumbnail_unauthenticated(
|
|||||||
|
|
||||||
let Response {
|
let Response {
|
||||||
file, content_type, content_disposition, ..
|
file, content_type, content_disposition, ..
|
||||||
} = self.federation_request_unauthenticated(mxc, server, request).await?;
|
} = self
|
||||||
|
.federation_request_legacy_media(mxc, server, request)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
||||||
|
|
||||||
@@ -168,7 +181,9 @@ async fn fetch_content_unauthenticated(
|
|||||||
|
|
||||||
let Response {
|
let Response {
|
||||||
file, content_type, content_disposition, ..
|
file, content_type, content_disposition, ..
|
||||||
} = self.federation_request_unauthenticated(mxc, server, request).await?;
|
} = self
|
||||||
|
.federation_request_legacy_media(mxc, server, request)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
let content = Content::new(file, content_type.unwrap(), content_disposition.unwrap());
|
||||||
|
|
||||||
@@ -300,7 +315,11 @@ async fn federation_request<'i, Request>(
|
|||||||
request: Request,
|
request: Request,
|
||||||
) -> Result<Request::IncomingResponse>
|
) -> Result<Request::IncomingResponse>
|
||||||
where
|
where
|
||||||
Request: OutgoingRequest::<Authentication = ServerSignatures, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Debug + Send,
|
Request: OutgoingRequest<
|
||||||
|
Authentication = ServerSignatures,
|
||||||
|
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||||
|
> + Debug
|
||||||
|
+ Send,
|
||||||
{
|
{
|
||||||
self.services
|
self.services
|
||||||
.sending
|
.sending
|
||||||
@@ -309,18 +328,22 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
async fn federation_request_unauthenticated<'i, Request>(
|
async fn federation_request_legacy_media<'i, Request>(
|
||||||
&self,
|
&self,
|
||||||
mxc: &Mxc<'_>,
|
mxc: &Mxc<'_>,
|
||||||
server: Option<&ServerName>,
|
server: Option<&ServerName>,
|
||||||
request: Request,
|
request: Request,
|
||||||
) -> Result<Request::IncomingResponse>
|
) -> Result<Request::IncomingResponse>
|
||||||
where
|
where
|
||||||
Request: OutgoingRequest::<Authentication = NoAuthentication, PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>> + Debug + Send,
|
Request: OutgoingRequest<
|
||||||
|
Authentication = NoAccessToken,
|
||||||
|
PathBuilder: PathBuilder<Input<'i>: FederationPathBuilderInput>,
|
||||||
|
> + Debug
|
||||||
|
+ Send,
|
||||||
{
|
{
|
||||||
self.services
|
self.services
|
||||||
.sending
|
.sending
|
||||||
.send_unauthenticated_request(server.unwrap_or(mxc.server_name), request)
|
.send_legacy_media_request(server.unwrap_or(mxc.server_name), request)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,7 +358,12 @@ pub async fn fetch_remote_thumbnail_legacy(
|
|||||||
media_id: &body.media_id,
|
media_id: &body.media_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut request = media::get_content_thumbnail::v3::Request::new(body.media_id.clone(), body.server_name.clone(), body.width, body.height);
|
let mut request = media::get_content_thumbnail::v3::Request::new(
|
||||||
|
body.media_id.clone(),
|
||||||
|
body.server_name.clone(),
|
||||||
|
body.width,
|
||||||
|
body.height,
|
||||||
|
);
|
||||||
request.method = body.method.clone();
|
request.method = body.method.clone();
|
||||||
request.allow_remote = body.allow_remote;
|
request.allow_remote = body.allow_remote;
|
||||||
request.allow_redirect = body.allow_redirect;
|
request.allow_redirect = body.allow_redirect;
|
||||||
@@ -347,7 +375,7 @@ pub async fn fetch_remote_thumbnail_legacy(
|
|||||||
let response = self
|
let response = self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_unauthenticated_request(mxc.server_name, request)
|
.send_legacy_media_request(mxc.server_name, request)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
let dim = Dim::from_ruma(body.width, body.height, body.method.clone())?;
|
||||||
@@ -372,7 +400,8 @@ pub async fn fetch_remote_content_legacy(
|
|||||||
allow_redirect: bool,
|
allow_redirect: bool,
|
||||||
timeout_ms: Duration,
|
timeout_ms: Duration,
|
||||||
) -> Result<media::get_content::v3::Response, Error> {
|
) -> Result<media::get_content::v3::Response, Error> {
|
||||||
let mut request = media::get_content::v3::Request::new(mxc.media_id.into(), mxc.server_name.into());
|
let mut request =
|
||||||
|
media::get_content::v3::Request::new(mxc.media_id.into(), mxc.server_name.into());
|
||||||
request.allow_remote = true;
|
request.allow_remote = true;
|
||||||
request.allow_redirect = allow_redirect;
|
request.allow_redirect = allow_redirect;
|
||||||
request.timeout_ms = timeout_ms;
|
request.timeout_ms = timeout_ms;
|
||||||
@@ -382,7 +411,7 @@ pub async fn fetch_remote_content_legacy(
|
|||||||
let response = self
|
let response = self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_unauthenticated_request(mxc.server_name, request)
|
.send_legacy_media_request(mxc.server_name, request)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let content_disposition = make_content_disposition(
|
let content_disposition = make_content_disposition(
|
||||||
|
|||||||
@@ -14,9 +14,8 @@ use tokio::{
|
|||||||
io::{AsyncReadExt, AsyncWriteExt},
|
io::{AsyncReadExt, AsyncWriteExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::media::mxc::Mxc;
|
|
||||||
|
|
||||||
use super::{FileMeta, data::Metadata};
|
use super::{FileMeta, data::Metadata};
|
||||||
|
use crate::media::mxc::Mxc;
|
||||||
|
|
||||||
/// Dimension specification for a thumbnail.
|
/// Dimension specification for a thumbnail.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|||||||
@@ -270,13 +270,7 @@ async fn migrate(services: &Services) -> Result<()> {
|
|||||||
{
|
{
|
||||||
let patterns = services.globals.forbidden_alias_names();
|
let patterns = services.globals.forbidden_alias_names();
|
||||||
if !patterns.is_empty() {
|
if !patterns.is_empty() {
|
||||||
for room_id in services
|
for room_id in services.rooms.metadata.iter_ids().collect::<Vec<_>>().await {
|
||||||
.rooms
|
|
||||||
.metadata
|
|
||||||
.iter_ids()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
.alias
|
.alias
|
||||||
@@ -479,12 +473,7 @@ async fn retroactively_fix_bad_data_from_roomuserid_joined(services: &Services)
|
|||||||
let db = &services.db;
|
let db = &services.db;
|
||||||
let _cork = db.cork_and_sync();
|
let _cork = db.cork_and_sync();
|
||||||
|
|
||||||
let room_ids = services
|
let room_ids = services.rooms.metadata.iter_ids().collect::<Vec<_>>().await;
|
||||||
.rooms
|
|
||||||
.metadata
|
|
||||||
.iter_ids()
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
for room_id in &room_ids {
|
for room_id in &room_ids {
|
||||||
debug_info!("Fixing room {room_id}");
|
debug_info!("Fixing room {room_id}");
|
||||||
|
|||||||
+3
-3
@@ -49,9 +49,9 @@ conduwuit::mod_ctor! {}
|
|||||||
conduwuit::mod_dtor! {}
|
conduwuit::mod_dtor! {}
|
||||||
|
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
||||||
use conduwuit::matrix::versions::{unstable_features, versions};
|
use conduwuit::matrix::versions::{unstable_features, versions};
|
||||||
use ruma::api::SupportedVersions;
|
use ruma::api::SupportedVersions;
|
||||||
|
|
||||||
pub static SUPPORTED_VERSIONS: LazyLock<SupportedVersions> = LazyLock::new(|| {
|
pub static SUPPORTED_VERSIONS: LazyLock<SupportedVersions> =
|
||||||
SupportedVersions::from_parts(&versions(), &unstable_features())
|
LazyLock::new(|| SupportedVersions::from_parts(&versions(), &unstable_features()));
|
||||||
});
|
|
||||||
|
|||||||
@@ -193,10 +193,7 @@ impl Service {
|
|||||||
| _ => continue,
|
| _ => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(
|
if matches!(presence.presence, PresenceState::Offline) {
|
||||||
presence.presence,
|
|
||||||
PresenceState::Offline
|
|
||||||
) {
|
|
||||||
trace!(%user_id, ?presence, "Skipping user");
|
trace!(%user_id, ?presence, "Skipping user");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,9 +54,6 @@ impl Presence {
|
|||||||
content.displayname = users.displayname(user_id).await.ok();
|
content.displayname = users.displayname(user_id).await.ok();
|
||||||
content.avatar_url = users.avatar_url(user_id).await.ok();
|
content.avatar_url = users.avatar_url(user_id).await.ok();
|
||||||
|
|
||||||
PresenceEvent {
|
PresenceEvent { sender: user_id.to_owned(), content }
|
||||||
sender: user_id.to_owned(),
|
|
||||||
content,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-13
@@ -11,17 +11,30 @@ use conduwuit_database::{Deserialized, Ignore, Interfix, Json, Map};
|
|||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use ipaddress::IPAddress;
|
use ipaddress::IPAddress;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
DeviceId, OwnedDeviceId, RoomId, UInt, UserId, api::{
|
DeviceId, OwnedDeviceId, RoomId, UInt, UserId,
|
||||||
IncomingResponse, MatrixVersion, OutgoingRequest, auth_scheme::{NoAuthentication, SendAccessToken}, client::push::{Pusher, PusherKind, set_pusher}, path_builder::SinglePath, push_gateway::send_event_notification::{
|
api::{
|
||||||
|
IncomingResponse, MatrixVersion, OutgoingRequest,
|
||||||
|
auth_scheme::{NoAccessToken, NoAuthentication, SendAccessToken},
|
||||||
|
client::push::{Pusher, PusherKind, set_pusher},
|
||||||
|
path_builder::SinglePath,
|
||||||
|
push_gateway::send_event_notification::{
|
||||||
self,
|
self,
|
||||||
v1::{Device, Notification, NotificationCounts, NotificationPriority},
|
v1::{Device, Notification, NotificationCounts, NotificationPriority},
|
||||||
}
|
},
|
||||||
}, events::{
|
},
|
||||||
|
events::{
|
||||||
AnySyncTimelineEvent, StateEventType, TimelineEventType,
|
AnySyncTimelineEvent, StateEventType, TimelineEventType,
|
||||||
room::{create::RoomCreateEventContent, power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}},
|
room::{
|
||||||
}, push::{
|
create::RoomCreateEventContent,
|
||||||
Action, PushConditionPowerLevelsCtx, PushConditionRoomCtx, PushFormat, Ruleset, Tweak,
|
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||||
}, room_version_rules::{AuthorizationRules, RoomPowerLevelsRules, RoomVersionRules}, serde::Raw, uint
|
},
|
||||||
|
},
|
||||||
|
push::{
|
||||||
|
Action, HighlightTweakValue, PushConditionPowerLevelsCtx, PushConditionRoomCtx, PushFormat, Ruleset, Tweak
|
||||||
|
},
|
||||||
|
room_version_rules::{AuthorizationRules, RoomPowerLevelsRules, RoomVersionRules},
|
||||||
|
serde::Raw,
|
||||||
|
uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Dep, client, config, globals, rooms, sending, users};
|
use crate::{Dep, client, config, globals, rooms, sending, users};
|
||||||
@@ -189,7 +202,9 @@ impl Service {
|
|||||||
#[tracing::instrument(skip(self, dest, request))]
|
#[tracing::instrument(skip(self, dest, request))]
|
||||||
pub async fn send_request<T>(&self, dest: &str, request: T) -> Result<T::IncomingResponse>
|
pub async fn send_request<T>(&self, dest: &str, request: T) -> Result<T::IncomingResponse>
|
||||||
where
|
where
|
||||||
T: OutgoingRequest<Authentication = NoAuthentication, PathBuilder = SinglePath> + Debug + Send,
|
T: OutgoingRequest<Authentication = NoAuthentication, PathBuilder = SinglePath>
|
||||||
|
+ Debug
|
||||||
|
+ Send,
|
||||||
{
|
{
|
||||||
const VERSIONS: [MatrixVersion; 1] = [MatrixVersion::V1_0];
|
const VERSIONS: [MatrixVersion; 1] = [MatrixVersion::V1_0];
|
||||||
|
|
||||||
@@ -197,7 +212,7 @@ impl Service {
|
|||||||
trace!("Push gateway destination: {dest}");
|
trace!("Push gateway destination: {dest}");
|
||||||
|
|
||||||
let http_request = request
|
let http_request = request
|
||||||
.try_into_http_request::<BytesMut>(&dest, SendAccessToken::None, ())
|
.try_into_http_request::<BytesMut>(&dest, (), ())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
err!(BadServerResponse(warn!(
|
err!(BadServerResponse(warn!(
|
||||||
"Failed to find destination {dest} for push gateway: {e}"
|
"Failed to find destination {dest} for push gateway: {e}"
|
||||||
@@ -307,7 +322,13 @@ impl Service {
|
|||||||
|
|
||||||
let serialized = event.to_format();
|
let serialized = event.to_format();
|
||||||
for action in self
|
for action in self
|
||||||
.get_actions(user, &ruleset, power_levels.clone(), &serialized, event.room_id().unwrap())
|
.get_actions(
|
||||||
|
user,
|
||||||
|
&ruleset,
|
||||||
|
power_levels.clone(),
|
||||||
|
&serialized,
|
||||||
|
event.room_id().unwrap(),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let n = match action {
|
let n = match action {
|
||||||
@@ -363,7 +384,13 @@ impl Service {
|
|||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| user.localpart().to_owned());
|
.unwrap_or_else(|_| user.localpart().to_owned());
|
||||||
|
|
||||||
let ctx = PushConditionRoomCtx::new(room_id.to_owned(), room_joined_count, user.to_owned(), user_display_name).with_power_levels(power_levels);
|
let ctx = PushConditionRoomCtx::new(
|
||||||
|
room_id.to_owned(),
|
||||||
|
room_joined_count,
|
||||||
|
user.to_owned(),
|
||||||
|
user_display_name,
|
||||||
|
)
|
||||||
|
.with_power_levels(power_levels);
|
||||||
|
|
||||||
ruleset.get_actions(pdu, &ctx).await
|
ruleset.get_actions(pdu, &ctx).await
|
||||||
}
|
}
|
||||||
@@ -442,7 +469,7 @@ impl Service {
|
|||||||
if *event.kind() == TimelineEventType::RoomEncrypted
|
if *event.kind() == TimelineEventType::RoomEncrypted
|
||||||
|| tweaks
|
|| tweaks
|
||||||
.iter()
|
.iter()
|
||||||
.any(|t| matches!(t, Tweak::Highlight(true) | Tweak::Sound(_)))
|
.any(|t| matches!(t, Tweak::Highlight(HighlightTweakValue::Yes) | Tweak::Sound(_)))
|
||||||
{
|
{
|
||||||
notify.prio = NotificationPriority::High;
|
notify.prio = NotificationPriority::High;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ use conduwuit::{
|
|||||||
use database::{Deserialized, Ignore, Interfix, Map};
|
use database::{Deserialized, Ignore, Interfix, Map};
|
||||||
use futures::{Stream, StreamExt, TryFutureExt};
|
use futures::{Stream, StreamExt, TryFutureExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomAliasId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, RoomId, RoomOrAliasId, UserId, events::{
|
OwnedRoomAliasId, OwnedRoomId, OwnedServerName, OwnedUserId, RoomAliasId, RoomId,
|
||||||
|
RoomOrAliasId, UserId,
|
||||||
|
events::{
|
||||||
StateEventType,
|
StateEventType,
|
||||||
room::power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
room::power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Dep, admin, appservice, appservice::RegistrationInfo, globals, rooms, sending};
|
use crate::{Dep, admin, appservice, appservice::RegistrationInfo, globals, rooms, sending};
|
||||||
|
|||||||
@@ -109,7 +109,10 @@ where
|
|||||||
match self
|
match self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(origin, get_event::v1::Request::new((*next_id).to_owned()))
|
.send_federation_request(
|
||||||
|
origin,
|
||||||
|
get_event::v1::Request::new((*next_id).to_owned()),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
| Ok(res) => {
|
| Ok(res) => {
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ where
|
|||||||
let res = self
|
let res = self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(origin, get_room_state_ids::v1::Request::new(event_id.to_owned(), room_id.to_owned()))
|
.send_federation_request(
|
||||||
|
origin,
|
||||||
|
get_room_state_ids::v1::Request::new(event_id.to_owned(), room_id.to_owned()),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.inspect_err(|e| debug_warn!("Fetching state for event failed: {e}"))?;
|
.inspect_err(|e| debug_warn!("Fetching state for event failed: {e}"))?;
|
||||||
|
|
||||||
|
|||||||
@@ -182,7 +182,8 @@ pub async fn handle_incoming_pdu<'a>(
|
|||||||
// copied from https://github.com/element-hq/synapse/blob/7e4588a/synapse/handlers/federation_event.py#L255-L300
|
// copied from https://github.com/element-hq/synapse/blob/7e4588a/synapse/handlers/federation_event.py#L255-L300
|
||||||
if value.get("type").and_then(|t| t.as_str()) == Some("m.room.member") {
|
if value.get("type").and_then(|t| t.as_str()) == Some("m.room.member") {
|
||||||
if let Some(pdu) =
|
if let Some(pdu) =
|
||||||
should_rescind_invite(&self.services, &mut value.clone(), &sender, room_id).await?
|
should_rescind_invite(&self.services, &mut value.clone(), &sender, room_id)
|
||||||
|
.await?
|
||||||
{
|
{
|
||||||
debug_info!(
|
debug_info!(
|
||||||
"Invite to {room_id} appears to have been rescinded by {sender}, marking as \
|
"Invite to {room_id} appears to have been rescinded by {sender}, marking as \
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ where
|
|||||||
// 2. Check signatures, otherwise drop
|
// 2. Check signatures, otherwise drop
|
||||||
// 3. check content hash, redact if doesn't match
|
// 3. check content hash, redact if doesn't match
|
||||||
let room_version = get_room_version(create_event)?;
|
let room_version = get_room_version(create_event)?;
|
||||||
let room_rules = room_version.rules().expect("room version should have defined rules");
|
let room_rules = room_version
|
||||||
|
.rules()
|
||||||
|
.expect("room version should have defined rules");
|
||||||
let mut incoming_pdu = match self
|
let mut incoming_pdu = match self
|
||||||
.services
|
.services
|
||||||
.server_keys
|
.server_keys
|
||||||
|
|||||||
@@ -119,4 +119,4 @@ fn get_room_version<Pdu: Event>(create_event: &Pdu) -> Result<RoomVersionId> {
|
|||||||
let room_version = content.room_version;
|
let room_version = content.room_version;
|
||||||
|
|
||||||
Ok(room_version)
|
Ok(room_version)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ use conduwuit::{
|
|||||||
matrix::event::{gen_event_id, gen_event_id_canonical_json},
|
matrix::event::{gen_event_id, gen_event_id_canonical_json},
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ruma::{CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, RoomVersionId};
|
use ruma::{
|
||||||
|
CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedRoomId, RoomVersionId, };
|
||||||
use serde_json::value::RawValue as RawJsonValue;
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
type Parsed = (OwnedRoomId, OwnedEventId, CanonicalJsonObject);
|
type Parsed = (OwnedRoomId, OwnedEventId, CanonicalJsonObject);
|
||||||
|
|||||||
@@ -10,16 +10,17 @@ use conduwuit::{
|
|||||||
warn,
|
warn,
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, CanonicalJsonValue, KeyId, OwnedKeyId, RoomId, ServerName, SigningKeyId, events::StateEventType
|
CanonicalJsonObject, CanonicalJsonValue, KeyId, OwnedKeyId, RoomId, ServerName, SigningKeyId,
|
||||||
|
events::StateEventType,
|
||||||
};
|
};
|
||||||
use ruminuwuity::policy::{
|
use ruminuwuity::policy::{
|
||||||
policy_check::unstable::Request as PolicyCheckRequest,
|
event::RoomPolicyEventContent, policy_check::unstable::Request as PolicyCheckRequest,
|
||||||
policy_sign::unstable::Request as PolicySignRequest,
|
policy_sign::unstable::Request as PolicySignRequest,
|
||||||
event::RoomPolicyEventContent
|
|
||||||
};
|
};
|
||||||
use serde_json::value::RawValue;
|
use serde_json::value::RawValue;
|
||||||
|
|
||||||
static POLICY_EVENT_TYPE_UNSTABLE: LazyLock<StateEventType> = LazyLock::new(|| StateEventType::from("org.matrix.msc4284.policy"));
|
static POLICY_EVENT_TYPE_UNSTABLE: LazyLock<StateEventType> =
|
||||||
|
LazyLock::new(|| StateEventType::from("org.matrix.msc4284.policy"));
|
||||||
|
|
||||||
/// Asks a remote policy server if the event is allowed.
|
/// Asks a remote policy server if the event is allowed.
|
||||||
///
|
///
|
||||||
@@ -88,7 +89,12 @@ pub async fn ask_policy_server(
|
|||||||
return Ok(true);
|
return Ok(true);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
if !self.services.state_cache.server_in_room(&via, room_id).await {
|
if !self
|
||||||
|
.services
|
||||||
|
.state_cache
|
||||||
|
.server_in_room(&via, room_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
debug!(
|
debug!(
|
||||||
via = %via,
|
via = %via,
|
||||||
"Policy server is not in the room, skipping spam check"
|
"Policy server is not in the room, skipping spam check"
|
||||||
@@ -132,15 +138,13 @@ pub async fn ask_policy_server(
|
|||||||
via = %via,
|
via = %via,
|
||||||
"Checking event for spam with policy server via legacy check"
|
"Checking event for spam with policy server via legacy check"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut request = PolicyCheckRequest::new(pdu.event_id().to_owned());
|
let mut request = PolicyCheckRequest::new(pdu.event_id().to_owned());
|
||||||
request.pdu = Some(outgoing);
|
request.pdu = Some(outgoing);
|
||||||
|
|
||||||
let response = tokio::time::timeout(
|
let response = tokio::time::timeout(
|
||||||
Duration::from_secs(self.services.server.config.policy_server_request_timeout),
|
Duration::from_secs(self.services.server.config.policy_server_request_timeout),
|
||||||
self.services
|
self.services.sending.send_federation_request(&via, request),
|
||||||
.sending
|
|
||||||
.send_federation_request(&via, request),
|
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let response = match response {
|
let response = match response {
|
||||||
|
|||||||
@@ -112,7 +112,13 @@ where
|
|||||||
{
|
{
|
||||||
let event_fetch = |event_id| self.event_fetch(event_id);
|
let event_fetch = |event_id| self.event_fetch(event_id);
|
||||||
let event_exists = |event_id| self.event_exists(event_id);
|
let event_exists = |event_id| self.event_exists(event_id);
|
||||||
state_res::resolve(room_version_rules, state_sets, auth_chain_sets, &event_fetch, &event_exists)
|
state_res::resolve(
|
||||||
.map_err(|e| err!(error!("State resolution failed: {e:?}")))
|
room_version_rules,
|
||||||
.await
|
state_sets,
|
||||||
|
auth_chain_sets,
|
||||||
|
&event_fetch,
|
||||||
|
&event_exists,
|
||||||
|
)
|
||||||
|
.map_err(|e| err!(error!("State resolution failed: {e:?}")))
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,9 @@ where
|
|||||||
);
|
);
|
||||||
let timer = Instant::now();
|
let timer = Instant::now();
|
||||||
let room_version_id = get_room_version(create_event)?;
|
let room_version_id = get_room_version(create_event)?;
|
||||||
let room_version_rules = room_version_id.rules().expect("room version should have defined rules");
|
let room_version_rules = room_version_id
|
||||||
|
.rules()
|
||||||
|
.expect("room version should have defined rules");
|
||||||
|
|
||||||
// 10. Fetch missing state and auth chain events by calling /state_ids at
|
// 10. Fetch missing state and auth chain events by calling /state_ids at
|
||||||
// backwards extremities doing all the checks in this list starting at 1.
|
// backwards extremities doing all the checks in this list starting at 1.
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ use conduwuit::{
|
|||||||
use database::{Deserialized, Json, Map};
|
use database::{Deserialized, Json, Map};
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, OwnedRoomId, OwnedUserId, RoomId, UserId, events::{AnySyncEphemeralRoomEvent, receipt::ReceiptEvent}, serde::Raw
|
CanonicalJsonObject, OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||||
|
events::{AnySyncEphemeralRoomEvent, receipt::ReceiptEvent},
|
||||||
|
serde::Raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Dep, globals};
|
use crate::{Dep, globals};
|
||||||
|
|||||||
@@ -17,16 +17,17 @@ use conduwuit_core::{
|
|||||||
use futures::{FutureExt, Stream, StreamExt, TryFutureExt, pin_mut, stream::FuturesUnordered};
|
use futures::{FutureExt, Stream, StreamExt, TryFutureExt, pin_mut, stream::FuturesUnordered};
|
||||||
use lru_cache::LruCache;
|
use lru_cache::LruCache;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId, api::{
|
OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, ServerName, UserId,
|
||||||
|
api::{
|
||||||
client::space::SpaceHierarchyRoomsChunk,
|
client::space::SpaceHierarchyRoomsChunk,
|
||||||
federation::{
|
federation::{self, space::SpaceHierarchyParentSummary},
|
||||||
self,
|
},
|
||||||
space::SpaceHierarchyParentSummary,
|
events::{
|
||||||
},
|
|
||||||
}, events::{
|
|
||||||
StateEventType,
|
StateEventType,
|
||||||
space::child::{HierarchySpaceChildEvent, SpaceChildEventContent},
|
space::child::{HierarchySpaceChildEvent, SpaceChildEventContent},
|
||||||
}, room::{JoinRuleSummary, RoomSummary}, serde::Raw,
|
},
|
||||||
|
room::{JoinRuleSummary, RoomSummary},
|
||||||
|
serde::Raw,
|
||||||
};
|
};
|
||||||
use tokio::sync::{Mutex, MutexGuard};
|
use tokio::sync::{Mutex, MutexGuard};
|
||||||
|
|
||||||
@@ -299,11 +300,7 @@ async fn get_room_summary(
|
|||||||
let join_rule = self.services.state_accessor.get_join_rules(room_id).await;
|
let join_rule = self.services.state_accessor.get_join_rules(room_id).await;
|
||||||
|
|
||||||
let is_accessible_child = self
|
let is_accessible_child = self
|
||||||
.is_accessible_child(
|
.is_accessible_child(room_id, &join_rule.clone().into(), identifier)
|
||||||
room_id,
|
|
||||||
&join_rule.clone().into(),
|
|
||||||
identifier,
|
|
||||||
)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if !is_accessible_child {
|
if !is_accessible_child {
|
||||||
@@ -375,7 +372,7 @@ async fn get_room_summary(
|
|||||||
join_rule.clone().into(),
|
join_rule.clone().into(),
|
||||||
guest_can_join,
|
guest_can_join,
|
||||||
num_joined_members.try_into().unwrap_or_default(),
|
num_joined_members.try_into().unwrap_or_default(),
|
||||||
world_readable
|
world_readable,
|
||||||
);
|
);
|
||||||
summary.canonical_alias = canonical_alias;
|
summary.canonical_alias = canonical_alias;
|
||||||
summary.name = name;
|
summary.name = name;
|
||||||
@@ -426,13 +423,17 @@ async fn is_accessible_child(
|
|||||||
| JoinRuleSummary::Public
|
| JoinRuleSummary::Public
|
||||||
| JoinRuleSummary::Knock
|
| JoinRuleSummary::Knock
|
||||||
| JoinRuleSummary::KnockRestricted(_) => true,
|
| JoinRuleSummary::KnockRestricted(_) => true,
|
||||||
| JoinRuleSummary::Restricted(restricted_summary) => {
|
| JoinRuleSummary::Restricted(restricted_summary) =>
|
||||||
(&restricted_summary.allowed_room_ids).stream().any(async |room| match identifier {
|
(&restricted_summary.allowed_room_ids)
|
||||||
| Identifier::UserId(user) => self.services.state_cache.is_joined(user, room).await,
|
.stream()
|
||||||
| Identifier::ServerName(server) => self.services.state_cache.server_in_room(server, room).await,
|
.any(async |room| match identifier {
|
||||||
}).await
|
| Identifier::UserId(user) =>
|
||||||
},
|
self.services.state_cache.is_joined(user, room).await,
|
||||||
_ => false
|
| Identifier::ServerName(server) =>
|
||||||
|
self.services.state_cache.server_in_room(server, room).await,
|
||||||
|
})
|
||||||
|
.await,
|
||||||
|
| _ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -463,10 +464,10 @@ async fn cache_insert(
|
|||||||
summary: RoomSummary,
|
summary: RoomSummary,
|
||||||
) {
|
) {
|
||||||
let children_state = self
|
let children_state = self
|
||||||
.get_space_child_events(&summary.room_id)
|
.get_space_child_events(&summary.room_id)
|
||||||
.map(Event::into_format)
|
.map(Event::into_format)
|
||||||
.collect()
|
.collect()
|
||||||
.await;
|
.await;
|
||||||
let summary = SpaceHierarchyParentSummary::new(summary, children_state);
|
let summary = SpaceHierarchyParentSummary::new(summary, children_state);
|
||||||
|
|
||||||
cache.insert(current_room.to_owned(), Some(CachedSpaceHierarchySummary { summary }));
|
cache.insert(current_room.to_owned(), Some(CachedSpaceHierarchySummary { summary }));
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use conduwuit::{Error, Result};
|
use conduwuit::{Error, Result};
|
||||||
use ruma::{UInt, api::client::error::ErrorKind};
|
use ruma::{UInt, api::error::ErrorKind};
|
||||||
|
|
||||||
use crate::rooms::short::ShortRoomId;
|
use crate::rooms::short::ShortRoomId;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use ruma::{
|
use ruma::{
|
||||||
UInt, api::federation::space::SpaceHierarchyParentSummary, owned_room_id, owned_server_name, room::{JoinRuleSummary, RoomSummary},
|
UInt,
|
||||||
|
api::federation::space::SpaceHierarchyParentSummary,
|
||||||
|
owned_room_id, owned_server_name,
|
||||||
|
room::{JoinRuleSummary, RoomSummary},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::rooms::spaces::{PaginationToken, get_parent_children_via};
|
use crate::rooms::spaces::{PaginationToken, get_parent_children_via};
|
||||||
@@ -9,7 +12,13 @@ use crate::rooms::spaces::{PaginationToken, get_parent_children_via};
|
|||||||
#[test]
|
#[test]
|
||||||
fn get_summary_children() {
|
fn get_summary_children() {
|
||||||
let summary = SpaceHierarchyParentSummary::new(
|
let summary = SpaceHierarchyParentSummary::new(
|
||||||
RoomSummary::new(owned_room_id!("!root:example.org"), JoinRuleSummary::Public, true, UInt::from(1_u32), true),
|
RoomSummary::new(
|
||||||
|
owned_room_id!("!root:example.org"),
|
||||||
|
JoinRuleSummary::Public,
|
||||||
|
true,
|
||||||
|
UInt::from(1_u32),
|
||||||
|
true,
|
||||||
|
),
|
||||||
vec![
|
vec![
|
||||||
serde_json::from_str(
|
serde_json::from_str(
|
||||||
r#"{
|
r#"{
|
||||||
@@ -55,7 +64,7 @@ fn get_summary_children() {
|
|||||||
}"#,
|
}"#,
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
]
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -17,10 +17,13 @@ use futures::{
|
|||||||
FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, future::join_all, pin_mut,
|
FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, future::join_all, pin_mut,
|
||||||
};
|
};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId, events::{
|
EventId, OwnedEventId, OwnedRoomId, RoomId, RoomVersionId, UserId,
|
||||||
|
events::{
|
||||||
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
AnyStrippedStateEvent, StateEventType, TimelineEventType,
|
||||||
room::create::RoomCreateEventContent,
|
room::create::RoomCreateEventContent,
|
||||||
}, room_version_rules::RoomVersionRules, serde::Raw
|
},
|
||||||
|
room_version_rules::RoomVersionRules,
|
||||||
|
serde::Raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -107,7 +110,7 @@ impl Service {
|
|||||||
.then(|shorteventid| {
|
.then(|shorteventid| {
|
||||||
self.services
|
self.services
|
||||||
.short
|
.short
|
||||||
.get_eventid_from_short::<Box<_>>(shorteventid)
|
.get_eventid_from_short::<OwnedEventId>(shorteventid)
|
||||||
})
|
})
|
||||||
.ignore_err();
|
.ignore_err();
|
||||||
|
|
||||||
@@ -426,8 +429,13 @@ impl Service {
|
|||||||
return Ok(HashMap::new());
|
return Ok(HashMap::new());
|
||||||
};
|
};
|
||||||
|
|
||||||
let auth_types =
|
let auth_types = state_res::auth_types_for_event(
|
||||||
state_res::auth_types_for_event(kind, sender, state_key, content, room_version_rules)?;
|
kind,
|
||||||
|
sender,
|
||||||
|
state_key,
|
||||||
|
content,
|
||||||
|
room_version_rules,
|
||||||
|
)?;
|
||||||
debug!(?auth_types, "Auth types for event");
|
debug!(?auth_types, "Auth types for event");
|
||||||
let sauthevents: HashMap<_, _> = auth_types
|
let sauthevents: HashMap<_, _> = auth_types
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -9,12 +9,25 @@ use async_trait::async_trait;
|
|||||||
use conduwuit::{Event, Result, err};
|
use conduwuit::{Event, Result, err};
|
||||||
use database::Map;
|
use database::Map;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
EventEncryptionAlgorithm, JsOption, OwnedRoomAliasId, OwnedUserId, RoomId, UserId, events::{
|
EventEncryptionAlgorithm, JsOption, OwnedRoomAliasId, OwnedUserId, RoomId, UserId,
|
||||||
|
events::{
|
||||||
StateEventType,
|
StateEventType,
|
||||||
room::{
|
room::{
|
||||||
avatar::RoomAvatarEventContent, canonical_alias::RoomCanonicalAliasEventContent, create::{RoomCreateEvent, RoomCreateEventContent}, encryption::RoomEncryptionEventContent, guest_access::{GuestAccess, RoomGuestAccessEventContent}, history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent}, join_rules::{JoinRule, RoomJoinRulesEventContent}, member::RoomMemberEventContent, name::RoomNameEventContent, pinned_events::RoomPinnedEventsEventContent, power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent}, topic::RoomTopicEventContent
|
avatar::RoomAvatarEventContent,
|
||||||
|
canonical_alias::RoomCanonicalAliasEventContent,
|
||||||
|
create::{RoomCreateEvent, RoomCreateEventContent},
|
||||||
|
encryption::RoomEncryptionEventContent,
|
||||||
|
guest_access::{GuestAccess, RoomGuestAccessEventContent},
|
||||||
|
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
|
||||||
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
||||||
|
member::RoomMemberEventContent,
|
||||||
|
name::RoomNameEventContent,
|
||||||
|
pinned_events::RoomPinnedEventsEventContent,
|
||||||
|
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
|
||||||
|
topic::RoomTopicEventContent,
|
||||||
},
|
},
|
||||||
}, room::RoomType
|
},
|
||||||
|
room::RoomType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Dep, rooms};
|
use crate::{Dep, rooms};
|
||||||
@@ -152,12 +165,25 @@ impl Service {
|
|||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a set of the room's creators. This will always contain a single user for room versions 11 and earlier.
|
/// Get a set of the room's creators. This will always contain a single user
|
||||||
|
/// for room versions 11 and earlier.
|
||||||
pub async fn get_room_creators(&self, room_id: &RoomId) -> HashSet<OwnedUserId> {
|
pub async fn get_room_creators(&self, room_id: &RoomId) -> HashSet<OwnedUserId> {
|
||||||
let room_version_rules = self.services.state.get_room_version(room_id).await.expect("room should have a version").rules().expect("room version should be known");
|
let room_version_rules = self
|
||||||
|
.services
|
||||||
|
.state
|
||||||
|
.get_room_version(room_id)
|
||||||
|
.await
|
||||||
|
.expect("room should have a version")
|
||||||
|
.rules()
|
||||||
|
.expect("room version should be known");
|
||||||
|
|
||||||
let create_event = self.room_state_get(room_id, &StateEventType::RoomCreate, "").await.expect("room should have a create event");
|
let create_event = self
|
||||||
let create_content: RoomCreateEventContent = create_event.get_content().expect("create event content should be valid");
|
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||||
|
.await
|
||||||
|
.expect("room should have a create event");
|
||||||
|
let create_content: RoomCreateEventContent = create_event
|
||||||
|
.get_content()
|
||||||
|
.expect("create event content should be valid");
|
||||||
|
|
||||||
let mut creators = HashSet::new();
|
let mut creators = HashSet::new();
|
||||||
if room_version_rules.authorization.use_room_create_sender {
|
if room_version_rules.authorization.use_room_create_sender {
|
||||||
@@ -174,15 +200,30 @@ impl Service {
|
|||||||
creators
|
creators
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the room's power levels. This will never fail -- if the room has no power level state event,
|
/// Get the room's power levels. This will never fail -- if the room has no
|
||||||
/// the default power levels for the room's version will be returned.
|
/// power level state event, the default power levels for the room's
|
||||||
|
/// version will be returned.
|
||||||
pub async fn get_room_power_levels(&self, room_id: &RoomId) -> RoomPowerLevels {
|
pub async fn get_room_power_levels(&self, room_id: &RoomId) -> RoomPowerLevels {
|
||||||
let room_version_rules = self.services.state.get_room_version(room_id).await.expect("room should have a version").rules().expect("room version should be known");
|
let room_version_rules = self
|
||||||
let creators = self.get_room_creators(room_id).await;
|
.services
|
||||||
let power_levels_event: RoomPowerLevelsEventContent = self.room_state_get_content(room_id, &StateEventType::RoomPowerLevels, "")
|
.state
|
||||||
|
.get_room_version(room_id)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_else(|_| RoomPowerLevelsEventContent::new(&room_version_rules.authorization));
|
.expect("room should have a version")
|
||||||
|
.rules()
|
||||||
|
.expect("room version should be known");
|
||||||
|
let creators = self.get_room_creators(room_id).await;
|
||||||
|
let power_levels_event: RoomPowerLevelsEventContent = self
|
||||||
|
.room_state_get_content(room_id, &StateEventType::RoomPowerLevels, "")
|
||||||
|
.await
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
RoomPowerLevelsEventContent::new(&room_version_rules.authorization)
|
||||||
|
});
|
||||||
|
|
||||||
RoomPowerLevels::new(power_levels_event.into(), &room_version_rules.authorization, creators)
|
RoomPowerLevels::new(
|
||||||
|
power_levels_event.into(),
|
||||||
|
&room_version_rules.authorization,
|
||||||
|
creators,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,17 +44,13 @@ pub async fn server_can_see_event(
|
|||||||
| HistoryVisibility::Invited => {
|
| HistoryVisibility::Invited => {
|
||||||
// Allow if any member on requesting server was AT LEAST invited, else deny
|
// Allow if any member on requesting server was AT LEAST invited, else deny
|
||||||
current_server_members
|
current_server_members
|
||||||
.any(async |member| {
|
.any(async |member| self.user_was_invited(shortstatehash, &member).await)
|
||||||
self.user_was_invited(shortstatehash, &member).await
|
|
||||||
})
|
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
| HistoryVisibility::Joined => {
|
| HistoryVisibility::Joined => {
|
||||||
// Allow if any member on requested server was joined, else deny
|
// Allow if any member on requested server was joined, else deny
|
||||||
current_server_members
|
current_server_members
|
||||||
.any(async |member| {
|
.any(async |member| self.user_was_joined(shortstatehash, &member).await)
|
||||||
self.user_was_joined(shortstatehash, &member).await
|
|
||||||
})
|
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
| HistoryVisibility::WorldReadable | HistoryVisibility::Shared | _ => true,
|
| HistoryVisibility::WorldReadable | HistoryVisibility::Shared | _ => true,
|
||||||
|
|||||||
@@ -316,7 +316,9 @@ pub fn state_full(
|
|||||||
shortstatehash: ShortStateHash,
|
shortstatehash: ShortStateHash,
|
||||||
) -> impl Stream<Item = ((StateEventType, StateKey), impl Event)> + Send + '_ {
|
) -> impl Stream<Item = ((StateEventType, StateKey), impl Event)> + Send + '_ {
|
||||||
self.state_full_pdus(shortstatehash)
|
self.state_full_pdus(shortstatehash)
|
||||||
.ready_filter_map(|pdu| Some(((pdu.kind().to_string().into(), pdu.state_key()?.into()), pdu)))
|
.ready_filter_map(|pdu| {
|
||||||
|
Some(((pdu.kind().to_string().into(), pdu.state_key()?.into()), pdu))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
|
|||||||
@@ -44,13 +44,22 @@ pub async fn user_can_redact(
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let create_event = self.room_state_get(room_id, &StateEventType::RoomCreate, "").await?;
|
let create_event = self
|
||||||
|
.room_state_get(room_id, &StateEventType::RoomCreate, "")
|
||||||
|
.await?;
|
||||||
let create_event_content: RoomCreateEventContent = create_event.get_content().unwrap();
|
let create_event_content: RoomCreateEventContent = create_event.get_content().unwrap();
|
||||||
let room_version_rules = create_event_content.room_version.rules().expect("room version should have defined rules");
|
let room_version_rules = create_event_content
|
||||||
if room_version_rules.authorization.explicitly_privilege_room_creators {
|
.room_version
|
||||||
|
.rules()
|
||||||
|
.expect("room version should have defined rules");
|
||||||
|
if room_version_rules
|
||||||
|
.authorization
|
||||||
|
.explicitly_privilege_room_creators
|
||||||
|
{
|
||||||
let sender_owned = sender.to_owned();
|
let sender_owned = sender.to_owned();
|
||||||
// NOTE: we don't check the pre-v11 `creator` field because no room version has
|
// NOTE: we don't check the pre-v11 `creator` field because no room version has
|
||||||
// `explicitly_privilege_room_creators` and `use_room_create_sender` set at the same time
|
// `explicitly_privilege_room_creators` and `use_room_create_sender` set at the
|
||||||
|
// same time
|
||||||
if sender == create_event.sender()
|
if sender == create_event.sender()
|
||||||
|| create_event_content
|
|| create_event_content
|
||||||
.additional_creators
|
.additional_creators
|
||||||
@@ -60,7 +69,6 @@ pub async fn user_can_redact(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let power_levels = self.get_room_power_levels(room_id).await;
|
let power_levels = self.get_room_power_levels(room_id).await;
|
||||||
|
|
||||||
if power_levels.user_can_redact_event_of_other(sender) {
|
if power_levels.user_can_redact_event_of_other(sender) {
|
||||||
@@ -69,14 +77,13 @@ pub async fn user_can_redact(
|
|||||||
|
|
||||||
if power_levels.user_can_redact_own_event(sender) {
|
if power_levels.user_can_redact_own_event(sender) {
|
||||||
let is_own_event = match redacting_event {
|
let is_own_event = match redacting_event {
|
||||||
Ok(redacting_event) => {
|
| Ok(redacting_event) =>
|
||||||
if federation {
|
if federation {
|
||||||
redacting_event.sender().server_name() == sender.server_name()
|
redacting_event.sender().server_name() == sender.server_name()
|
||||||
} else {
|
} else {
|
||||||
redacting_event.sender() == sender
|
redacting_event.sender() == sender
|
||||||
}
|
},
|
||||||
},
|
| _ => false,
|
||||||
_ => false
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(is_own_event);
|
return Ok(is_own_event);
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ use conduwuit::{
|
|||||||
use database::{Deserialized, Ignore, Interfix, Map};
|
use database::{Deserialized, Ignore, Interfix, Map};
|
||||||
use futures::{Stream, StreamExt, future::join5, pin_mut};
|
use futures::{Stream, StreamExt, future::join5, pin_mut};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId, events::{AnyStrippedStateEvent, room::member::MembershipState}, serde::Raw
|
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
|
||||||
|
events::{AnyStrippedStateEvent, room::member::MembershipState},
|
||||||
|
serde::Raw,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{Dep, account_data, appservice::RegistrationInfo, config, globals, rooms, users};
|
use crate::{Dep, account_data, appservice::RegistrationInfo, config, globals, rooms, users};
|
||||||
@@ -415,7 +417,9 @@ pub async fn invite_state(
|
|||||||
.qry(&key)
|
.qry(&key)
|
||||||
.await
|
.await
|
||||||
.deserialized()
|
.deserialized()
|
||||||
.and_then(|val: Raw<Vec<AnyStrippedStateEvent>>| val.deserialize_as_unchecked().map_err(Into::into))
|
.and_then(|val: Raw<Vec<AnyStrippedStateEvent>>| {
|
||||||
|
val.deserialize_as_unchecked().map_err(Into::into)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(Service)]
|
#[implement(Service)]
|
||||||
@@ -431,7 +435,9 @@ pub async fn knock_state(
|
|||||||
.qry(&key)
|
.qry(&key)
|
||||||
.await
|
.await
|
||||||
.deserialized()
|
.deserialized()
|
||||||
.and_then(|val: Raw<Vec<AnyStrippedStateEvent>>| val.deserialize_as_unchecked().map_err(Into::into))
|
.and_then(|val: Raw<Vec<AnyStrippedStateEvent>>| {
|
||||||
|
val.deserialize_as_unchecked().map_err(Into::into)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(Service)]
|
#[implement(Service)]
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ use conduwuit_core::{
|
|||||||
use conduwuit_database::{Deserialized, Map};
|
use conduwuit_database::{Deserialized, Map};
|
||||||
use futures::{Stream, StreamExt};
|
use futures::{Stream, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonValue, EventId, OwnedUserId, RoomId, UserId, api::client::threads::get_threads::v1::IncludeThreads, events::relation::BundledThread, serde::Raw, uint
|
CanonicalJsonValue, EventId, OwnedUserId, RoomId, UserId,
|
||||||
|
api::client::threads::get_threads::v1::IncludeThreads, events::relation::BundledThread,
|
||||||
|
serde::Raw, uint,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
@@ -100,7 +102,11 @@ impl Service {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// New thread
|
// New thread
|
||||||
let relations = BundledThread::new(Raw::from_json(event.content().to_owned()), uint!(1), true);
|
let relations = BundledThread::new(
|
||||||
|
Raw::from_json(event.content().to_owned()),
|
||||||
|
uint!(1),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
let content = serde_json::to_value(relations).expect("to_value always works");
|
let content = serde_json::to_value(relations).expect("to_value always works");
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,7 @@ use ruma::{
|
|||||||
events::{
|
events::{
|
||||||
GlobalAccountDataEventType, TimelineEventType,
|
GlobalAccountDataEventType, TimelineEventType,
|
||||||
push_rules::PushRulesEvent,
|
push_rules::PushRulesEvent,
|
||||||
room::{
|
room::{encrypted::Relation, redaction::RoomRedactionEventContent},
|
||||||
encrypted::Relation,
|
|
||||||
redaction::RoomRedactionEventContent,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
push::{Action, Ruleset, Tweak},
|
push::{Action, Ruleset, Tweak},
|
||||||
};
|
};
|
||||||
@@ -204,7 +201,11 @@ where
|
|||||||
drop(insert_lock);
|
drop(insert_lock);
|
||||||
|
|
||||||
// See if the event matches any known pushers via power level
|
// See if the event matches any known pushers via power level
|
||||||
let power_levels = self.services.state_accessor.get_room_power_levels(room_id).await;
|
let power_levels = self
|
||||||
|
.services
|
||||||
|
.state_accessor
|
||||||
|
.get_room_power_levels(room_id)
|
||||||
|
.await;
|
||||||
let mut push_target: HashSet<_> = self
|
let mut push_target: HashSet<_> = self
|
||||||
.services
|
.services
|
||||||
.state_cache
|
.state_cache
|
||||||
@@ -251,7 +252,7 @@ where
|
|||||||
{
|
{
|
||||||
match action {
|
match action {
|
||||||
| Action::Notify => notify = true,
|
| Action::Notify => notify = true,
|
||||||
| Action::SetTweak(Tweak::Highlight(true)) => {
|
| Action::SetTweak(Tweak::Highlight(ruma::push::HighlightTweakValue::Yes)) => {
|
||||||
highlight = true;
|
highlight = true;
|
||||||
},
|
},
|
||||||
| _ => {},
|
| _ => {},
|
||||||
@@ -366,10 +367,12 @@ where
|
|||||||
|
|
||||||
if let Ok(content) = pdu.get_content::<ExtractRelatesTo>() {
|
if let Ok(content) = pdu.get_content::<ExtractRelatesTo>() {
|
||||||
match content.relates_to {
|
match content.relates_to {
|
||||||
| Relation::Reply { in_reply_to } => {
|
| Relation::Reply(in_reply_to) => {
|
||||||
// We need to do it again here, because replies don't have
|
// We need to do it again here, because replies don't have
|
||||||
// event_id as a top level field
|
// event_id as a top level field
|
||||||
if let Ok(related_pducount) = self.get_pdu_count(&in_reply_to.event_id).await {
|
if let Ok(related_pducount) =
|
||||||
|
self.get_pdu_count(&in_reply_to.in_reply_to.event_id).await
|
||||||
|
{
|
||||||
self.services
|
self.services
|
||||||
.pdu_metadata
|
.pdu_metadata
|
||||||
.add_relation(count2, related_pducount);
|
.add_relation(count2, related_pducount);
|
||||||
|
|||||||
@@ -12,10 +12,16 @@ use conduwuit_core::{
|
|||||||
};
|
};
|
||||||
use futures::{FutureExt, Stream, StreamExt};
|
use futures::{FutureExt, Stream, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, EventId, Int, OwnedServerName, RoomId, ServerName, api::federation, events::{
|
CanonicalJsonObject, EventId, Int, OwnedServerName, RoomId, ServerName,
|
||||||
|
api::federation,
|
||||||
|
events::{
|
||||||
StateEventType, TimelineEventType,
|
StateEventType, TimelineEventType,
|
||||||
room::{create::RoomCreateEventContent, power_levels::{RoomPowerLevelsEventContent, UserPowerLevel}},
|
room::{
|
||||||
}, uint
|
create::RoomCreateEventContent,
|
||||||
|
power_levels::{RoomPowerLevelsEventContent, UserPowerLevel},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
uint,
|
||||||
};
|
};
|
||||||
use serde_json::value::RawValue as RawJsonValue;
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
@@ -66,7 +72,11 @@ pub async fn backfill_if_required(&self, room_id: &RoomId, from: PduCount) -> Re
|
|||||||
.sending
|
.sending
|
||||||
.send_federation_request(
|
.send_federation_request(
|
||||||
&backfill_server,
|
&backfill_server,
|
||||||
federation::backfill::get_backfill::v1::Request::new(room_id.to_owned(), vec![first_pdu.1.event_id().to_owned()], uint!(100))
|
federation::backfill::get_backfill::v1::Request::new(
|
||||||
|
room_id.to_owned(),
|
||||||
|
vec![first_pdu.1.event_id().to_owned()],
|
||||||
|
uint!(100),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
match response {
|
match response {
|
||||||
@@ -125,7 +135,10 @@ pub async fn get_remote_pdu(&self, room_id: &RoomId, event_id: &EventId) -> Resu
|
|||||||
let value = self
|
let value = self
|
||||||
.services
|
.services
|
||||||
.sending
|
.sending
|
||||||
.send_federation_request(&backfill_server, federation::event::get_event::v1::Request::new(event_id.to_owned()))
|
.send_federation_request(
|
||||||
|
&backfill_server,
|
||||||
|
federation::event::get_event::v1::Request::new(event_id.to_owned()),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
.and_then(|response| {
|
.and_then(|response| {
|
||||||
serde_json::from_str::<CanonicalJsonObject>(response.pdu.get()).map_err(|e| {
|
serde_json::from_str::<CanonicalJsonObject>(response.pdu.get()).map_err(|e| {
|
||||||
@@ -220,7 +233,11 @@ pub async fn backfill_pdu(&self, origin: &ServerName, pdu: Box<RawJsonValue>) ->
|
|||||||
async fn candidate_backfill_servers(&self, room_id: &RoomId) -> HashSet<OwnedServerName> {
|
async fn candidate_backfill_servers(&self, room_id: &RoomId) -> HashSet<OwnedServerName> {
|
||||||
let mut candidate_backfill_servers = HashSet::new();
|
let mut candidate_backfill_servers = HashSet::new();
|
||||||
|
|
||||||
let power_levels = self.services.state_accessor.get_room_power_levels(room_id).await;
|
let power_levels = self
|
||||||
|
.services
|
||||||
|
.state_accessor
|
||||||
|
.get_room_power_levels(room_id)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Insert servers of room creators
|
// Insert servers of room creators
|
||||||
if let Some(creators) = &power_levels.rules.privileged_creators {
|
if let Some(creators) = &power_levels.rules.privileged_creators {
|
||||||
@@ -237,19 +254,30 @@ async fn candidate_backfill_servers(&self, room_id: &RoomId) -> HashSet<OwnedSer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert the canonical room alias server
|
// Insert the canonical room alias server
|
||||||
if let Ok(canonical_alias) = self.services.state_accessor.get_canonical_alias(room_id).await {
|
if let Ok(canonical_alias) = self
|
||||||
|
.services
|
||||||
|
.state_accessor
|
||||||
|
.get_canonical_alias(room_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
candidate_backfill_servers.insert(canonical_alias.server_name().to_owned());
|
candidate_backfill_servers.insert(canonical_alias.server_name().to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert all trusted servers in the config
|
// Insert all trusted servers in the config
|
||||||
candidate_backfill_servers.extend(self.services.server.config.trusted_servers.iter().cloned());
|
candidate_backfill_servers
|
||||||
|
.extend(self.services.server.config.trusted_servers.iter().cloned());
|
||||||
|
|
||||||
// Remove our own name, we can't request backfill from ourselves
|
// Remove our own name, we can't request backfill from ourselves
|
||||||
candidate_backfill_servers.remove(self.services.globals.server_name());
|
candidate_backfill_servers.remove(self.services.globals.server_name());
|
||||||
|
|
||||||
// Remove all servers that aren't in the room
|
// Remove all servers that aren't in the room
|
||||||
for server in candidate_backfill_servers.clone() {
|
for server in candidate_backfill_servers.clone() {
|
||||||
if !self.services.state_cache.server_in_room(&server, room_id).await {
|
if !self
|
||||||
|
.services
|
||||||
|
.state_cache
|
||||||
|
.server_in_room(&server, room_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
candidate_backfill_servers.remove(&server);
|
candidate_backfill_servers.remove(&server);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ pub async fn build_and_append_pdu(
|
|||||||
body,
|
body,
|
||||||
Some(pdu.event_id().into()),
|
Some(pdu.event_id().into()),
|
||||||
source,
|
source,
|
||||||
pdu.sender.clone().into(),
|
pdu.sender.clone(),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ pub async fn create_event(
|
|||||||
event_type,
|
event_type,
|
||||||
room_id.as_ref().map_or("None", |id| id.as_str())
|
room_id.as_ref().map_or("None", |id| id.as_str())
|
||||||
);
|
);
|
||||||
let room_version_id = match room_id {
|
let room_version = match room_id {
|
||||||
| Some(room_id) => {
|
| Some(room_id) => {
|
||||||
trace!(%room_id, "Looking up existing room ID");
|
trace!(%room_id, "Looking up existing room ID");
|
||||||
self.services
|
self.services
|
||||||
@@ -114,7 +114,7 @@ pub async fn create_event(
|
|||||||
| None => {
|
| None => {
|
||||||
trace!("No room ID, assuming room creation");
|
trace!("No room ID, assuming room creation");
|
||||||
room_version_from_event(
|
room_version_from_event(
|
||||||
RoomId::new(self.services.globals.server_name()),
|
RoomId::new_v1(self.services.globals.server_name()),
|
||||||
&event_type.clone(),
|
&event_type.clone(),
|
||||||
&content.clone(),
|
&content.clone(),
|
||||||
)?
|
)?
|
||||||
@@ -261,7 +261,7 @@ pub async fn create_event(
|
|||||||
pdu.event_id,
|
pdu.event_id,
|
||||||
pdu.room_id.as_ref().map_or("None", |id| id.as_str())
|
pdu.room_id.as_ref().map_or("None", |id| id.as_str())
|
||||||
);
|
);
|
||||||
Ok((pdu, room_version_id))
|
Ok((pdu, room_version))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
@@ -276,7 +276,7 @@ pub async fn create_hash_and_sign_event(
|
|||||||
if !self.services.globals.user_is_local(sender) {
|
if !self.services.globals.user_is_local(sender) {
|
||||||
return Err!(Request(Forbidden("Sender must be a local user")));
|
return Err!(Request(Forbidden("Sender must be a local user")));
|
||||||
}
|
}
|
||||||
let (mut pdu, room_version_id) = self
|
let (mut pdu, room_version) = self
|
||||||
.create_event(pdu_builder, sender, room_id, mutex_lock)
|
.create_event(pdu_builder, sender, room_id, mutex_lock)
|
||||||
.await?;
|
.await?;
|
||||||
// Hash and sign
|
// Hash and sign
|
||||||
@@ -292,7 +292,7 @@ pub async fn create_hash_and_sign_event(
|
|||||||
.hash_and_sign_event(&mut pdu_json, &room_version)
|
.hash_and_sign_event(&mut pdu_json, &room_version)
|
||||||
{
|
{
|
||||||
return match e {
|
return match e {
|
||||||
| Error::Signatures(ruma::signatures::Error::PduSize) => {
|
| Error::SignatureJson(ruma::signatures::JsonError::PduTooLarge) => {
|
||||||
Err!(Request(TooLarge("Message/PDU is too long (exceeds 65535 bytes)")))
|
Err!(Request(TooLarge("Message/PDU is too long (exceeds 65535 bytes)")))
|
||||||
},
|
},
|
||||||
| _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))),
|
| _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user