diff --git a/Cargo.lock b/Cargo.lock index c8d56884..c82646a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1632,6 +1632,16 @@ dependencies = [ "litrs", ] +[[package]] +name = "draupnir-antispam" +version = "0.1.0" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" +dependencies = [ + "ruma-common", + "serde", + "serde_json", +] + [[package]] name = "dtor" version = "0.1.0" @@ -2985,7 +2995,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "meowlnir-antispam" version = "0.1.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "ruma-common", "serde", @@ -4075,9 +4085,10 @@ checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" [[package]] name = "ruma" version = "0.10.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "assign", + "draupnir-antispam", "js_int", "js_option", "meowlnir-antispam", @@ -4096,7 +4107,7 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.10.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "js_int", "ruma-common", @@ -4108,7 +4119,7 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "as_variant", "assign", @@ -4131,7 +4142,7 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "as_variant", "base64 0.22.1", @@ -4163,7 +4174,7 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "as_variant", "indexmap", @@ -4188,7 +4199,7 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "bytes", "headers", @@ -4210,7 +4221,7 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "js_int", "thiserror 2.0.17", @@ -4219,7 +4230,7 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "js_int", "ruma-common", @@ -4229,7 +4240,7 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "cfg-if", "proc-macro-crate", @@ -4244,7 +4255,7 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "js_int", "ruma-common", @@ -4256,7 +4267,7 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.15.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=377d801fa035480b772c640b430097c1ec0ddb16#377d801fa035480b772c640b430097c1ec0ddb16" +source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=46e31bd6439eccbd3a1762f710c17fc15168c15e#46e31bd6439eccbd3a1762f710c17fc15168c15e" dependencies = [ "base64 0.22.1", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 3ab0b5c0..202ea15e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -351,7 +351,7 @@ version = "0.1.2" # Used for matrix spec type definitions and helpers [workspace.dependencies.ruma] git = "https://forgejo.ellis.link/continuwuation/ruwuma" -rev = "377d801fa035480b772c640b430097c1ec0ddb16" +rev = "46e31bd6439eccbd3a1762f710c17fc15168c15e" features = [ "compat", "rand", diff --git a/conduwuit-example.toml b/conduwuit-example.toml index 77b89002..ad25d4a7 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -1647,7 +1647,7 @@ # Enable the tokio-console. This option is only relevant to developers. # -# For more information, see: +# For more information, see: # https://continuwuity.org/development.html#debugging-with-tokio-console # #tokio_console = false @@ -1757,10 +1757,6 @@ # #ldap = false -# Configuration for antispam support -# -#antispam = false - [global.tls] # Path to a valid TLS certificate file. @@ -1930,7 +1926,7 @@ [global.antispam.meowlnir] -# The base URL on which to contact meowlnir (before /_meowlnir/antispam). +# The base URL on which to contact Meowlnir (before /_meowlnir/antispam). # # Example: "http://127.0.0.1:29339" # @@ -1944,3 +1940,24 @@ # The management room for which to send requests # #management_room = + +# If enabled run all federated join attempts (both federated and local) +# through the Meowlnir anti-spam checks. +# +# By default, only join attempts for rooms with the `fi.mau.spam_checker` +# restricted join rule are checked. +# +#check_all_joins = false + +[global.antispam.draupnir] + +# The base URL on which to contact Draupnir (before /api/). +# +# Example: "http://127.0.0.1:29339" +# +#base_url = + +# The authentication secret defined in +# web->synapseHTTPAntispam->authorization +# +#secret = diff --git a/src/api/client/membership/invite.rs b/src/api/client/membership/invite.rs index 2a546475..8b4f6d65 100644 --- a/src/api/client/membership/invite.rs +++ b/src/api/client/membership/invite.rs @@ -1,11 +1,9 @@ use axum::extract::State; use axum_client_ip::InsecureClientIp; use conduwuit::{ - Err, Result, - config::Antispam, - debug_error, err, info, + Err, Result, debug_error, err, info, matrix::{event::gen_event_id_canonical_json, pdu::PduBuilder}, - trace, + warn, }; use futures::FutureExt; use ruma::{ @@ -15,7 +13,6 @@ use ruma::{ invite_permission_config::FilterLevel, room::member::{MembershipState, RoomMemberEventContent}, }, - meowlnir_antispam::user_may_invite, }; use service::Services; @@ -128,24 +125,16 @@ pub(crate) async fn invite_helper( return Err!(Request(Forbidden("Invites are not allowed on this server."))); } - trace!("maybe ask meowlnir"); - if let Some(Antispam { meowlnir: Some(cfg) }) = &services.config.antispam { - trace!("asking meowlnir"); - services - .sending - .send_meowlnir_antispam_request( - cfg, - user_may_invite::v1::Request::new( - cfg.management_room.clone(), - sender_user.to_owned(), - recipient_user.to_owned(), - ), - ) - .await - .inspect(|_| trace!("meowlnir :D")) - .inspect_err(|e| debug_error!("meowlnir sad: {e}"))?; - } else { - trace!("no meowlnir configured"); + if let Err(e) = services + .antispam + .user_may_invite(sender_user.to_owned(), recipient_user.to_owned(), room_id.to_owned()) + .await + { + warn!( + "Invite from {} to {} in room {} blocked by antispam: {e:?}", + sender_user, recipient_user, room_id + ); + return Err!(Request(Forbidden("Invite blocked by antispam service."))); } if !services.globals.user_is_local(recipient_user) { diff --git a/src/api/client/membership/join.rs b/src/api/client/membership/join.rs index 25c7d663..5f53520b 100644 --- a/src/api/client/membership/join.rs +++ b/src/api/client/membership/join.rs @@ -3,9 +3,7 @@ use std::{borrow::Borrow, collections::HashMap, iter::once, sync::Arc}; use axum::extract::State; use axum_client_ip::InsecureClientIp; use conduwuit::{ - Err, Result, - config::Antispam, - debug, debug_info, debug_warn, err, error, info, + Err, Result, debug, debug_info, debug_warn, err, error, info, matrix::{ StateKey, event::{gen_event_id, gen_event_id_canonical_json}, @@ -39,7 +37,6 @@ use ruma::{ member::{MembershipState, RoomMemberEventContent}, }, }, - meowlnir_antispam::user_may_join_room, }; use service::{ Services, @@ -82,6 +79,26 @@ pub(crate) async fn join_room_by_id_route( ) .await?; + if let Err(e) = services + .antispam + .user_may_join_room( + sender_user.to_owned(), + body.room_id.clone(), + services + .rooms + .state_cache + .is_invited(sender_user, &body.room_id) + .await, + ) + .await + { + warn!( + "Antispam prevented user {} from joining room {}: {}", + sender_user, body.room_id, e + ); + return Err!(Request(Forbidden("You are not allowed to join this room."))); + } + // There is no body.server_name for /roomId/join let mut servers: Vec<_> = services .rooms @@ -350,20 +367,6 @@ pub async fn join_room_by_id_helper( .boxed() .await?; } - - if let Some(Antispam { meowlnir: Some(cfg) }) = &services.config.antispam { - services - .sending - .send_meowlnir_antispam_request( - cfg, - user_may_join_room::v1::Request::new( - cfg.management_room.clone(), - sender_user.to_owned(), - room_id.to_owned(), - ), - ) - .await?; - } Ok(join_room_by_id::v3::Response::new(room_id.to_owned())) } diff --git a/src/api/server/invite.rs b/src/api/server/invite.rs index 09b20d58..0182be33 100644 --- a/src/api/server/invite.rs +++ b/src/api/server/invite.rs @@ -2,9 +2,7 @@ use axum::extract::State; use axum_client_ip::InsecureClientIp; use base64::{Engine as _, engine::general_purpose}; use conduwuit::{ - Err, Error, PduEvent, Result, - config::Antispam, - err, + Err, Error, PduEvent, Result, err, matrix::{Event, event::gen_event_id}, utils::{self, hash::sha256}, warn, @@ -13,7 +11,6 @@ use ruma::{ CanonicalJsonValue, OwnedUserId, UserId, api::{client::error::ErrorKind, federation::membership::create_invite}, events::room::member::{MembershipState, RoomMemberEventContent}, - meowlnir_antispam::user_may_invite, serde::JsonObject, }; @@ -151,18 +148,13 @@ pub(crate) async fn create_invite_route( return Err!(Request(Forbidden("This server does not allow room invites."))); } - if let Some(Antispam { meowlnir: Some(cfg) }) = &services.config.antispam { - services - .sending - .send_meowlnir_antispam_request( - cfg, - user_may_invite::v1::Request::new( - cfg.management_room.clone(), - sender_user.to_owned(), - recipient_user.clone(), - ), - ) - .await?; + if let Err(e) = services + .antispam + .user_may_invite(sender_user.to_owned(), recipient_user.clone(), body.room_id.clone()) + .await + { + warn!("Antispam rejected invite: {e:?}"); + return Err!(Request(Forbidden("Invite rejected by antispam service."))); } let mut invite_state = body.invite_room_state.clone(); diff --git a/src/api/server/make_join.rs b/src/api/server/make_join.rs index ce46caab..a506c5bc 100644 --- a/src/api/server/make_join.rs +++ b/src/api/server/make_join.rs @@ -1,7 +1,7 @@ +use std::borrow::ToOwned; + use axum::extract::State; -use conduwuit::{ - Err, Error, Result, debug_info, info, matrix::pdu::PduBuilder, utils::IterStream, warn, -}; +use conduwuit::{Err, Error, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn}; use conduwuit_service::Services; use futures::StreamExt; use ruma::{ @@ -136,7 +136,6 @@ pub(crate) async fn create_join_event_template_route( &state_lock, ) .await?; - drop(state_lock); // room v3 and above removed the "event_id" field from remote PDU format @@ -192,25 +191,52 @@ pub(crate) async fn user_can_perform_restricted_join( return Ok(false); } - if r.allow - .iter() - .filter_map(|rule| { - if let AllowRule::RoomMembership(membership) = rule { - Some(membership) - } else { - None - } - }) - .stream() - .any(|m| services.rooms.state_cache.is_joined(user_id, &m.room_id)) - .await - { - Ok(true) - } else { - Err!(Request(UnableToAuthorizeJoin( - "Joining user is not known to be in any required room." - ))) + for allow_rule in &r.allow { + match allow_rule { + | AllowRule::RoomMembership(membership) => { + if services + .rooms + .state_cache + .is_joined(user_id, &membership.room_id) + .await + { + debug!( + "User {} is allowed to join room {} via membership in room {}", + user_id, room_id, membership.room_id + ); + return Ok(true); + } + }, + | AllowRule::UnstableSpamChecker => { + match services + .antispam + .meowlnir_accept_make_join(room_id.to_owned(), user_id.to_owned()) + .await + { + | Ok(()) => { + return Ok(true); + }, + | Err(e) => { + info!( + "meowlnir rejected restricted join for user {} into room {}: {e:?}", + user_id, room_id + ); + }, + } + }, + | _ => { + debug_info!( + "Unsupported allow rule in restricted join for room {}: {:?}", + room_id, + allow_rule + ); + }, + } } + + Err!(Request(UnableToAuthorizeJoin( + "Joining user is not known to be in any required room." + ))) } pub(crate) fn maybe_strip_event_id( diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index 6b474d4e..60921b0e 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -53,7 +53,8 @@ use crate::{Result, err, error::Error, utils::sys}; ### For more information, see: ### https://continuwuity.org/configuration.html "#, - ignore = "config_paths catchall well_known tls blurhashing allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure" + ignore = "config_paths catchall well_known tls blurhashing \ + allow_invalid_tls_certificates_yes_i_know_what_the_fuck_i_am_doing_with_this_and_i_know_this_is_insecure antispam" )] pub struct Config { // Paths to config file(s). Not supposed to be set manually in the config file, @@ -1887,7 +1888,7 @@ pub struct Config { /// Enable the tokio-console. This option is only relevant to developers. /// - /// For more information, see: + /// For more information, see: /// https://continuwuity.org/development.html#debugging-with-tokio-console #[serde(default)] pub tokio_console: bool, @@ -2247,6 +2248,7 @@ struct ListeningAddr { #[derive(Clone, Debug, Deserialize)] pub struct Antispam { pub meowlnir: Option, + pub draupnir: Option, } #[derive(Clone, Debug, Deserialize)] @@ -2255,7 +2257,7 @@ pub struct Antispam { section = "global.antispam.meowlnir" )] pub struct MeowlnirConfig { - /// The base URL on which to contact meowlnir (before /_meowlnir/antispam). + /// The base URL on which to contact Meowlnir (before /_meowlnir/antispam). /// /// Example: "http://127.0.0.1:29339" pub base_url: Url, @@ -2266,6 +2268,32 @@ pub struct MeowlnirConfig { /// The management room for which to send requests pub management_room: OwnedRoomId, + + /// If enabled run all federated join attempts (both federated and local) + /// through the Meowlnir anti-spam checks. + /// + /// By default, only join attempts for rooms with the `fi.mau.spam_checker` + /// restricted join rule are checked. + #[serde(default)] + pub check_all_joins: bool, +} + +// TODO: the DraupnirConfig and MeowlnirConfig are basically identical. +// Maybe management_room could just become an Option<> and these structs merged? +#[derive(Clone, Debug, Deserialize)] +#[config_example_generator( + filename = "conduwuit-example.toml", + section = "global.antispam.draupnir" +)] +pub struct DraupnirConfig { + /// The base URL on which to contact Draupnir (before /api/). + /// + /// Example: "http://127.0.0.1:29339" + pub base_url: Url, + + /// The authentication secret defined in + /// web->synapseHTTPAntispam->authorization + pub secret: String, } const DEPRECATED_KEYS: &[&str; 9] = &[ diff --git a/src/service/antispam/mod.rs b/src/service/antispam/mod.rs new file mode 100644 index 00000000..d8711690 --- /dev/null +++ b/src/service/antispam/mod.rs @@ -0,0 +1,172 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use conduwuit::{Result, config::Antispam, debug}; +use ruma::{OwnedRoomId, OwnedUserId, draupnir_antispam, meowlnir_antispam}; + +use crate::{client, config, sending, service::Dep}; + +struct Services { + config: Dep, + client: Dep, +} + +pub struct Service { + services: Services, +} + +#[async_trait] +impl crate::Service for Service { + fn build(args: crate::Args<'_>) -> Result> { + Ok(Arc::new(Self { + services: Services { + client: args.depend::("client"), + config: args.depend::("config"), + }, + })) + } + + fn name(&self) -> &str { crate::service::make_name(std::module_path!()) } +} + +impl Service { + async fn send_antispam_request( + &self, + base_url: &str, + secret: &str, + request: T, + ) -> Result + where + T: ruma::api::OutgoingRequest + std::fmt::Debug + Send, + { + sending::antispam::send_antispam_request( + &self.services.client.appservice, + base_url, + secret, + request, + ) + .await + } + + /// Checks with the antispam service whether `inviter` may invite `invitee` + /// to `room_id`. + /// + /// If no antispam service is configured, this always returns `Ok(())`. + /// If an error is returned, the invite should be blocked - the antispam + /// service was unreachable, or refused the invite. + pub async fn user_may_invite( + &self, + inviter: OwnedUserId, + invitee: OwnedUserId, + room_id: OwnedRoomId, + ) -> Result<()> { + if let Some(config) = &self.services.config.antispam { + let result = if let Some(meowlnir) = &config.meowlnir { + debug!("Asking meowlnir for user_may_invite"); + self.send_antispam_request( + meowlnir.base_url.as_str(), + &meowlnir.secret, + meowlnir_antispam::user_may_invite::v1::Request::new( + meowlnir.management_room.clone(), + inviter, + invitee, + room_id, + ), + ) + .await + .inspect(|_| debug!("meowlnir allowed the invite")) + .inspect_err(|e| debug!("meowlnir denied the invite: {e:?}")) + .map(|_| ()) + } else if let Some(draupnir) = &config.draupnir { + debug!("Asking draupnir for user_may_invite"); + self.send_antispam_request( + draupnir.base_url.as_str(), + &draupnir.secret, + draupnir_antispam::user_may_invite::v1::Request::new( + room_id, inviter, invitee, + ), + ) + .await + .inspect(|_| debug!("draupnir allowed the invite")) + .inspect_err(|e| debug!("draupnir denied the invite: {e:?}")) + .map(|_| ()) + } else { + Ok(()) + }; + return result; + } + Ok(()) + } + + /// Checks with the antispam service whether `user_id` may join `room_id`. + pub async fn user_may_join_room( + &self, + user_id: OwnedUserId, + room_id: OwnedRoomId, + is_invited: bool, + ) -> Result<()> { + if let Some(config) = &self.services.config.antispam { + let result = if let Some(meowlnir) = &config.meowlnir { + debug!("Asking meowlnir for user_may_join_room"); + self.send_antispam_request( + meowlnir.base_url.as_str(), + &meowlnir.secret, + meowlnir_antispam::user_may_join_room::v1::Request::new( + meowlnir.management_room.clone(), + user_id, + room_id, + is_invited, + ), + ) + .await + .inspect(|_| debug!("meowlnir allowed the join")) + .inspect_err(|e| debug!("meowlnir denied the join: {e:?}")) + .map(|_| ()) + } else if let Some(draupnir) = &config.draupnir { + debug!("Asking draupnir for user_may_join_room"); + self.send_antispam_request( + draupnir.base_url.as_str(), + &draupnir.secret, + draupnir_antispam::user_may_join_room::v1::Request::new( + user_id, room_id, is_invited, + ), + ) + .await + .inspect(|_| debug!("draupnir allowed the join")) + .inspect_err(|e| debug!("draupnir denied the join: {e:?}")) + .map(|_| ()) + } else { + Ok(()) + }; + return result; + } + Ok(()) + } + + /// Checks with Meowlnir whether the incoming federated `make_join` request + /// should be allowed. Applies the `fi.mau.spam_checker` join rule. + pub async fn meowlnir_accept_make_join( + &self, + room_id: OwnedRoomId, + user_id: OwnedUserId, + ) -> Result<()> { + if let Some(Antispam { meowlnir: Some(meowlnir), .. }) = &self.services.config.antispam { + debug!("Asking meowlnir for meowlnir_accept_make_join"); + self.send_antispam_request( + meowlnir.base_url.as_str(), + &meowlnir.secret, + meowlnir_antispam::accept_make_join::v1::Request::new( + meowlnir.management_room.clone(), + user_id, + room_id, + ), + ) + .await + .inspect(|_| debug!("meowlnir allowed the make_join")) + .inspect_err(|e| debug!("meowlnir denied the make_join: {e:?}")) + .map(|_| ()) + } else { + Ok(()) + } + } +} diff --git a/src/service/mod.rs b/src/service/mod.rs index 2ad0ecd2..1e2af7a4 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -1,6 +1,8 @@ #![type_length_limit = "8192"] #![allow(refining_impl_trait)] +extern crate conduwuit_core as conduwuit; +extern crate conduwuit_database as database; mod manager; mod migrations; mod service; @@ -10,6 +12,7 @@ pub mod state; pub mod account_data; pub mod admin; pub mod announcements; +pub mod antispam; pub mod appservice; pub mod client; pub mod config; @@ -30,9 +33,6 @@ pub mod transaction_ids; pub mod uiaa; pub mod users; -extern crate conduwuit_core as conduwuit; -extern crate conduwuit_database as database; - use ctor::{ctor, dtor}; pub(crate) use service::{Args, Dep, Service}; diff --git a/src/service/sending/antispam.rs b/src/service/sending/antispam.rs index c21b29a7..2e328b1b 100644 --- a/src/service/sending/antispam.rs +++ b/src/service/sending/antispam.rs @@ -1,30 +1,23 @@ use std::{fmt::Debug, mem}; use bytes::BytesMut; -use conduwuit::{Err, Result, config::MeowlnirConfig, debug_error, err, utils, warn}; +use conduwuit::{Err, Result, debug_error, err, utils, warn}; use reqwest::Client; use ruma::api::{IncomingResponse, MatrixVersion, OutgoingRequest, SendAccessToken}; /// Sends a request to an antispam service -pub(crate) async fn send_meowlnir_request( +pub(crate) async fn send_antispam_request( client: &Client, - config: &MeowlnirConfig, + base_url: &str, + secret: &str, request: T, -) -> Result> +) -> Result where T: OutgoingRequest + Debug + Send, { const VERSIONS: [MatrixVersion; 1] = [MatrixVersion::V1_15]; - if config.secret.is_empty() { - return Ok(None); - } - let secret = config.secret.as_str(); let http_request = request - .try_into_http_request::( - config.base_url.as_str(), - SendAccessToken::Always(secret), - &VERSIONS, - )? + .try_into_http_request::(base_url, SendAccessToken::Always(secret), &VERSIONS)? .map(BytesMut::freeze); let reqwest_request = reqwest::Request::try_from(http_request)?; @@ -64,7 +57,7 @@ where .expect("reqwest body is valid http body"), ); - response.map(Some).map_err(|e| { + response.map_err(|e| { err!(BadServerResponse(warn!( "Antispam returned invalid/malformed response bytes: {e}", ))) diff --git a/src/service/sending/mod.rs b/src/service/sending/mod.rs index b82818c4..a0ec50ed 100644 --- a/src/service/sending/mod.rs +++ b/src/service/sending/mod.rs @@ -1,4 +1,4 @@ -mod antispam; +pub mod antispam; mod appservice; mod data; mod dest; @@ -13,9 +13,7 @@ use std::{ use async_trait::async_trait; use conduwuit::{ - Result, Server, - config::MeowlnirConfig, - debug, debug_warn, err, error, + Result, Server, debug, debug_warn, err, error, smallvec::SmallVec, utils::{ReadyExt, TryReadyExt, available_parallelism, math::usize_from_u64_truncated}, warn, @@ -337,18 +335,6 @@ impl Service { appservice::send_request(client, registration, request).await } - /// Sends a request to the chosen antispam configuration - pub async fn send_meowlnir_antispam_request( - &self, - config: &MeowlnirConfig, - request: T, - ) -> Result> - where - T: OutgoingRequest + Debug + Send, - { - antispam::send_meowlnir_request(&self.services.client.appservice, config, request).await - } - /// Clean up queued sending event data /// /// Used after we remove an appservice registration or a user deletes a push diff --git a/src/service/services.rs b/src/service/services.rs index 642f61c7..5b71c536 100644 --- a/src/service/services.rs +++ b/src/service/services.rs @@ -8,8 +8,8 @@ use futures::{Stream, StreamExt, TryStreamExt}; use tokio::sync::Mutex; use crate::{ - account_data, admin, announcements, appservice, client, config, emergency, federation, - globals, key_backups, + account_data, admin, announcements, antispam, appservice, client, config, emergency, + federation, globals, key_backups, manager::Manager, media, moderation, presence, pusher, resolver, rooms, sending, server_keys, service, service::{Args, Map, Service}, @@ -39,6 +39,7 @@ pub struct Services { pub users: Arc, pub moderation: Arc, pub announcements: Arc, + pub antispam: Arc, manager: Mutex>>, pub(crate) service: Arc, @@ -107,6 +108,7 @@ impl Services { users: build!(users::Service), moderation: build!(moderation::Service), announcements: build!(announcements::Service), + antispam: build!(antispam::Service), manager: Mutex::new(None), service,