diff --git a/Cargo.lock b/Cargo.lock index 92044b92..82e7a20d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3798,7 +3798,7 @@ dependencies = [ [[package]] name = "ruma" version = "0.10.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "assign", "js_int", @@ -3818,7 +3818,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.10.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "js_int", "ruma-common", @@ -3830,7 +3830,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "as_variant", "assign", @@ -3853,7 +3853,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "as_variant", "base64 0.22.1", @@ -3885,7 +3885,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "as_variant", "indexmap 2.9.0", @@ -3910,7 +3910,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "bytes", "headers", @@ -3932,7 +3932,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "js_int", "thiserror 2.0.12", @@ -3941,7 +3941,7 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "js_int", "ruma-common", @@ -3951,7 +3951,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "cfg-if", "proc-macro-crate", @@ -3966,7 +3966,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "js_int", "ruma-common", @@ -3978,7 +3978,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.15.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=9b65f83981f6f53d185ce77da37aaef9dfd764a9#9b65f83981f6f53d185ce77da37aaef9dfd764a9" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=a4b948b40417a65ab0282ae47cc50035dd455e02#a4b948b40417a65ab0282ae47cc50035dd455e02" dependencies = [ "base64 0.22.1", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 5c289adf..b815e2b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -350,7 +350,7 @@ version = "0.1.2" [workspace.dependencies.ruma] git = "https://forgejo.ellis.link/continuwuation/ruwuma" #branch = "conduwuit-changes" -rev = "9b65f83981f6f53d185ce77da37aaef9dfd764a9" +rev = "a4b948b40417a65ab0282ae47cc50035dd455e02" features = [ "compat", "rand", diff --git a/src/api/client/report.rs b/src/api/client/report.rs index 4ee8ebe5..5113b42f 100644 --- a/src/api/client/report.rs +++ b/src/api/client/report.rs @@ -1,4 +1,4 @@ -use std::time::Duration; +use std::{fmt::Write as _, ops::Mul, time::Duration}; use axum::extract::State; use axum_client_ip::InsecureClientIp; @@ -6,18 +6,29 @@ use conduwuit::{Err, Error, Result, debug_info, info, matrix::pdu::PduEvent, uti use conduwuit_service::Services; use rand::Rng; use ruma::{ - EventId, RoomId, UserId, + EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, api::client::{ error::ErrorKind, + report_user, room::{report_content, report_room}, }, - events::room::message, + events::{Mentions, room::message::RoomMessageEventContent}, int, }; use tokio::time::sleep; use crate::Ruma; +struct Report { + sender: OwnedUserId, + room_id: Option, + event_id: Option, + user_id: Option, + report_type: String, + reason: Option, + score: Option, +} + /// # `POST /_matrix/client/v3/rooms/{roomId}/report` /// /// Reports an abusive room to homeserver admins @@ -29,12 +40,9 @@ pub(crate) async fn report_room_route( ) -> Result { // user authentication let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - - info!( - "Received room report by user {sender_user} for room {} with reason: \"{}\"", - body.room_id, - body.reason.as_deref().unwrap_or("") - ); + if services.users.is_suspended(sender_user).await? { + return Err!(Request(UserSuspended("You cannot perform this action while suspended."))); + } if body.reason.as_ref().is_some_and(|s| s.len() > 750) { return Err(Error::BadRequest( @@ -55,19 +63,23 @@ pub(crate) async fn report_room_route( "Room does not exist to us, no local users have joined at all" ))); } + info!( + "Received room report by user {sender_user} for room {} with reason: \"{}\"", + body.room_id, + body.reason.as_deref().unwrap_or("") + ); - // send admin room message that we received the report with an @room ping for - // urgency - services - .admin - .send_message(message::RoomMessageEventContent::text_markdown(format!( - "@room Room report received from {} -\n\nRoom ID: {}\n\nReport Reason: {}", - sender_user.to_owned(), - body.room_id, - body.reason.as_deref().unwrap_or("") - ))) - .await - .ok(); + let report = Report { + sender: sender_user.to_owned(), + room_id: Some(body.room_id.clone()), + event_id: None, + user_id: None, + report_type: "room".to_owned(), + reason: body.reason.clone(), + score: None, + }; + + services.admin.send_message(build_report(report)).await.ok(); Ok(report_room::v3::Response {}) } @@ -83,14 +95,9 @@ pub(crate) async fn report_event_route( ) -> Result { // user authentication let sender_user = body.sender_user.as_ref().expect("user is authenticated"); - - info!( - "Received event report by user {sender_user} for room {} and event ID {}, with reason: \ - \"{}\"", - body.room_id, - body.event_id, - body.reason.as_deref().unwrap_or("") - ); + if services.users.is_suspended(sender_user).await? { + return Err!(Request(UserSuspended("You cannot perform this action while suspended."))); + } delay_response().await; @@ -109,27 +116,74 @@ pub(crate) async fn report_event_route( &pdu, ) .await?; - - // send admin room message that we received the report with an @room ping for - // urgency - services - .admin - .send_message(message::RoomMessageEventContent::text_markdown(format!( - "@room Event report received from {} -\n\nEvent ID: {}\nRoom ID: {}\nSent By: \ - {}\n\nReport Score: {}\nReport Reason: {}", - sender_user.to_owned(), - pdu.event_id, - pdu.room_id, - pdu.sender, - body.score.unwrap_or_else(|| ruma::Int::from(0)), - body.reason.as_deref().unwrap_or("") - ))) - .await - .ok(); + info!( + "Received event report by user {sender_user} for room {} and event ID {}, with reason: \ + \"{}\"", + body.room_id, + body.event_id, + body.reason.as_deref().unwrap_or("") + ); + let report = Report { + sender: sender_user.to_owned(), + room_id: Some(body.room_id.clone()), + event_id: Some(body.event_id.clone()), + user_id: None, + report_type: "event".to_owned(), + reason: body.reason.clone(), + score: body.score, + }; + services.admin.send_message(build_report(report)).await.ok(); Ok(report_content::v3::Response {}) } +#[tracing::instrument(skip_all, fields(%client), name = "report_user")] +pub(crate) async fn report_user_route( + State(services): State, + InsecureClientIp(client): InsecureClientIp, + body: Ruma, +) -> Result { + // user authentication + let sender_user = body.sender_user.as_ref().expect("user is authenticated"); + if services.users.is_suspended(sender_user).await? { + return Err!(Request(UserSuspended("You cannot perform this action while suspended."))); + } + + if body.reason.as_ref().is_some_and(|s| s.len() > 750) { + return Err(Error::BadRequest( + ErrorKind::InvalidParam, + "Reason too long, should be 750 characters or fewer", + )); + } + + delay_response().await; + + if !services.users.is_active_local(&body.user_id).await { + // return 200 as to not reveal if the user exists. Recommended by spec. + return Ok(report_user::v3::Response {}); + } + + let report = Report { + sender: sender_user.to_owned(), + room_id: None, + event_id: None, + user_id: Some(body.user_id.clone()), + report_type: "user".to_owned(), + reason: body.reason.clone(), + score: None, + }; + + info!( + "Received room report from {sender_user} for user {} with reason: \"{}\"", + body.user_id, + body.reason.as_deref().unwrap_or("") + ); + + services.admin.send_message(build_report(report)).await.ok(); + + Ok(report_user::v3::Response {}) +} + /// in the following order: /// /// check if the room ID from the URI matches the PDU's room ID @@ -187,6 +241,29 @@ async fn is_event_report_valid( Ok(()) } +/// Builds a report message to be sent to the admin room. +fn build_report(report: Report) -> RoomMessageEventContent { + let mut text = + format!("@room New {} report received from {}:\n\n", report.report_type, report.sender); + if report.user_id.is_some() { + let _ = writeln!(text, "- Reported User ID: `{}`", report.user_id.unwrap()); + } + if report.room_id.is_some() { + let _ = writeln!(text, "- Reported Room ID: `{}`", report.room_id.unwrap()); + } + if report.event_id.is_some() { + let _ = writeln!(text, "- Reported Event ID: `{}`", report.event_id.unwrap()); + } + if let Some(score) = report.score { + let _ = writeln!(text, "- User-supplied offensiveness score: {}%", score.mul(int!(-1))); + } + if let Some(reason) = report.reason { + let _ = writeln!(text, "- Report Reason: {reason}"); + } + + RoomMessageEventContent::text_markdown(text).add_mentions(Mentions::with_room_mention()) +} + /// even though this is kinda security by obscurity, let's still make a small /// random delay sending a response per spec suggestion regarding /// enumerating for potential events existing in our server. diff --git a/src/api/client/unversioned.rs b/src/api/client/unversioned.rs index 232d5b28..ad377ca4 100644 --- a/src/api/client/unversioned.rs +++ b/src/api/client/unversioned.rs @@ -38,6 +38,7 @@ pub(crate) async fn get_supported_versions_route( "v1.4".to_owned(), "v1.5".to_owned(), "v1.11".to_owned(), + "v1.14".to_owned(), ], unstable_features: BTreeMap::from_iter([ ("org.matrix.e2e_cross_signing".to_owned(), true), diff --git a/src/api/router.rs b/src/api/router.rs index 5416e9e9..d1b05a91 100644 --- a/src/api/router.rs +++ b/src/api/router.rs @@ -94,6 +94,7 @@ pub fn build(router: Router, server: &Server) -> Router { .ruma_route(&client::redact_event_route) .ruma_route(&client::report_event_route) .ruma_route(&client::report_room_route) + .ruma_route(&client::report_user_route) .ruma_route(&client::create_alias_route) .ruma_route(&client::delete_alias_route) .ruma_route(&client::get_alias_route)