diff --git a/crates/app/src/macros.rs b/crates/app/src/macros.rs index 01406bb..056a803 100644 --- a/crates/app/src/macros.rs +++ b/crates/app/src/macros.rs @@ -352,7 +352,10 @@ macro_rules! ignore_users_gen { ($user:ident, $data:ident) => { if let Some(ref ua) = $user { [ - $data.0.get_userblocks_receivers(ua.id).await, + $data + .0 + .get_userblocks_receivers(ua.id, &ua.associated) + .await, $data.0.get_userblocks_initiator_by_receivers(ua.id).await, $data.0.get_user_stack_blocked_users(ua.id).await, ] @@ -364,7 +367,10 @@ macro_rules! ignore_users_gen { ($user:ident!, $data:ident) => {{ [ - $data.0.get_userblocks_receivers($user.id).await, + $data + .0 + .get_userblocks_receivers($user.id, &$user.associated) + .await, $data .0 .get_userblocks_initiator_by_receivers($user.id) @@ -376,7 +382,9 @@ macro_rules! ignore_users_gen { ($user:ident!, #$data:ident) => { [ - $data.get_userblocks_receivers($user.id).await, + $data + .get_userblocks_receivers($user.id, &$user.associated) + .await, $data.get_userblocks_initiator_by_receivers($user.id).await, ] .concat() diff --git a/crates/app/src/routes/api/v1/auth/mod.rs b/crates/app/src/routes/api/v1/auth/mod.rs index a332dd8..934f5fc 100644 --- a/crates/app/src/routes/api/v1/auth/mod.rs +++ b/crates/app/src/routes/api/v1/auth/mod.rs @@ -54,7 +54,7 @@ pub async fn register_request( // check for ip ban if data - .get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) + .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str())) .await .is_ok() { @@ -189,7 +189,7 @@ pub async fn login_request( // check for ip ban if data - .get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) + .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str())) .await .is_ok() { diff --git a/crates/app/src/routes/api/v1/auth/social.rs b/crates/app/src/routes/api/v1/auth/social.rs index badbbc2..730746a 100644 --- a/crates/app/src/routes/api/v1/auth/social.rs +++ b/crates/app/src/routes/api/v1/auth/social.rs @@ -11,6 +11,7 @@ use axum::{ }; use axum_extra::extract::CookieJar; use tetratto_core::model::{ + addr::RemoteAddr, auth::{AchievementName, FollowResult, IpBlock, Notification, UserBlock, UserFollow}, oauth, }; @@ -228,7 +229,10 @@ pub async fn ip_block_request( None => return Json(Error::NotAllowed.into()), }; - if let Ok(ipblock) = data.get_ipblock_by_initiator_receiver(user.id, &ip).await { + if let Ok(ipblock) = data + .get_ipblock_by_initiator_receiver(user.id, &RemoteAddr::from(ip.as_str())) + .await + { // delete match data.delete_ipblock(ipblock.id, user).await { Ok(_) => Json(ApiReturn { @@ -335,7 +339,7 @@ pub async fn ip_block_profile_request( for (ip, _, _) in other_user.tokens { // check for an existing ip block if data - .get_ipblock_by_initiator_receiver(user.id, &ip) + .get_ipblock_by_initiator_receiver(user.id, &RemoteAddr::from(ip.as_str())) .await .is_ok() { diff --git a/crates/app/src/routes/api/v1/communities/posts.rs b/crates/app/src/routes/api/v1/communities/posts.rs index 1982d6e..e2e608e 100644 --- a/crates/app/src/routes/api/v1/communities/posts.rs +++ b/crates/app/src/routes/api/v1/communities/posts.rs @@ -67,7 +67,7 @@ pub async fn create_request( // check for ip ban if data - .get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) + .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str())) .await .is_ok() { diff --git a/crates/app/src/routes/api/v1/communities/questions.rs b/crates/app/src/routes/api/v1/communities/questions.rs index ec24c08..13566e5 100644 --- a/crates/app/src/routes/api/v1/communities/questions.rs +++ b/crates/app/src/routes/api/v1/communities/questions.rs @@ -43,7 +43,7 @@ pub async fn create_request( // check for ip ban if data - .get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) + .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str())) .await .is_ok() { @@ -145,7 +145,7 @@ pub async fn ip_block_request( // check for an existing ip block if data - .get_ipblock_by_initiator_receiver(user.id, &question.ip) + .get_ipblock_by_initiator_receiver(user.id, &RemoteAddr::from(question.ip.as_str())) .await .is_ok() { diff --git a/crates/app/src/routes/api/v1/reactions.rs b/crates/app/src/routes/api/v1/reactions.rs index b3efe52..261a48d 100644 --- a/crates/app/src/routes/api/v1/reactions.rs +++ b/crates/app/src/routes/api/v1/reactions.rs @@ -1,7 +1,12 @@ use crate::{State, get_user_from_token, routes::api::v1::CreateReaction}; -use axum::{Extension, Json, extract::Path, response::IntoResponse}; +use axum::{ + extract::Path, + http::{HeaderMap, HeaderValue}, + response::IntoResponse, + Extension, Json, +}; use axum_extra::extract::CookieJar; -use tetratto_core::model::{oauth, ApiReturn, Error, reactions::Reaction}; +use tetratto_core::model::{addr::RemoteAddr, oauth, reactions::Reaction, ApiReturn, Error}; pub async fn get_request( jar: CookieJar, @@ -26,6 +31,7 @@ pub async fn get_request( pub async fn create_request( jar: CookieJar, + headers: HeaderMap, Extension(data): Extension, Json(req): Json, ) -> impl IntoResponse { @@ -40,6 +46,20 @@ pub async fn create_request( Err(e) => return Json(Error::MiscError(e.to_string()).into()), }; + // get real ip + let real_ip = headers + .get(data.0.0.security.real_ip_header.to_owned()) + .unwrap_or(&HeaderValue::from_static("")) + .to_str() + .unwrap_or("") + .to_string(); + + // check for ip ban + let addr = RemoteAddr::from(real_ip.as_str()); + if data.get_ipban_by_addr(&addr).await.is_ok() { + return Json(Error::NotAllowed.into()); + } + // check for existing reaction if let Ok(r) = data.get_reaction_by_owner_asset(user.id, asset_id).await { match data.delete_reaction(r.id, &user).await { @@ -63,6 +83,7 @@ pub async fn create_request( .create_reaction( Reaction::new(user.id, asset_id, req.asset_type, req.is_like), &user, + &addr, ) .await { diff --git a/crates/app/src/routes/pages/communities.rs b/crates/app/src/routes/pages/communities.rs index e11b685..30d2ce0 100644 --- a/crates/app/src/routes/pages/communities.rs +++ b/crates/app/src/routes/pages/communities.rs @@ -3,14 +3,16 @@ use crate::{ assets::initial_context, check_user_blocked_or_private, get_lang, get_user_from_token, State, }; use axum::{ - Extension, extract::{Path, Query}, + http::{HeaderMap, HeaderValue}, response::{Html, IntoResponse}, + Extension, }; use axum_extra::extract::CookieJar; use serde::Deserialize; use tera::Context; use tetratto_core::model::{ + addr::RemoteAddr, auth::User, communities::Community, communities_permissions::CommunityPermission, @@ -642,6 +644,7 @@ pub async fn settings_request( /// `/post/{id}` pub async fn post_request( jar: CookieJar, + headers: HeaderMap, Path(id): Path, Query(props): Query, Extension(data): Extension, @@ -751,6 +754,46 @@ pub async fn post_request( check_user_blocked_or_private!(user, owner, data, jar); } + // get real ip + let real_ip = headers + .get(data.0.0.0.security.real_ip_header.to_owned()) + .unwrap_or(&HeaderValue::from_static("")) + .to_str() + .unwrap_or("") + .to_string(); + + // check for ip ban + let addr = RemoteAddr::from(real_ip.as_str()); + if data.0.get_ipban_by_addr(&addr).await.is_ok() { + return Err(Html( + render_error( + Error::GeneralNotFound("post".to_string()), + &jar, + &data, + &user, + ) + .await, + )); + } + + // check for ip block + if data + .0 + .get_ipblock_by_initiator_receiver(post.owner, &addr) + .await + .is_ok() + { + return Err(Html( + render_error( + Error::GeneralNotFound("post".to_string()), + &jar, + &data, + &user, + ) + .await, + )); + } + // check repost let (_, reposting) = data.0.get_post_reposting(&post, &ignore_users, &user).await; diff --git a/crates/core/src/database/ipbans.rs b/crates/core/src/database/ipbans.rs index 550570d..4748424 100644 --- a/crates/core/src/database/ipbans.rs +++ b/crates/core/src/database/ipbans.rs @@ -23,7 +23,7 @@ impl DataManager { /// /// # Arguments /// * `prefix` - pub async fn get_ipban_by_addr(&self, addr: RemoteAddr) -> Result { + pub async fn get_ipban_by_addr(&self, addr: &RemoteAddr) -> Result { let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), diff --git a/crates/core/src/database/ipblocks.rs b/crates/core/src/database/ipblocks.rs index 9eaf892..c3e92b2 100644 --- a/crates/core/src/database/ipblocks.rs +++ b/crates/core/src/database/ipblocks.rs @@ -1,10 +1,8 @@ use oiseau::cache::Cache; +use crate::model::addr::RemoteAddr; use crate::model::{Error, Result, auth::User, auth::IpBlock, permissions::FinePermission}; use crate::{auto_method, DataManager}; - -use oiseau::{query_rows, PostgresRow}; - -use oiseau::{execute, get, query_row, params}; +use oiseau::{query_rows, PostgresRow, execute, get, query_row, params}; impl DataManager { /// Get an [`IpBlock`] from an SQL row. @@ -23,7 +21,7 @@ impl DataManager { pub async fn get_ipblock_by_initiator_receiver( &self, initiator: usize, - receiver: &str, + receiver: &RemoteAddr, ) -> Result { let conn = match self.0.connect().await { Ok(c) => c, @@ -32,8 +30,8 @@ impl DataManager { let res = query_row!( &conn, - "SELECT * FROM ipblocks WHERE initiator = $1 AND receiver = $2", - params![&(initiator as i64), &receiver], + "SELECT * FROM ipblocks WHERE initiator = $1 AND receiver LIKE $2", + params![&(initiator as i64), &format!("{}%", receiver.prefix(None))], |x| { Ok(Self::get_ipblock_from_row(x)) } ); diff --git a/crates/core/src/database/questions.rs b/crates/core/src/database/questions.rs index a6ca60a..1cee527 100644 --- a/crates/core/src/database/questions.rs +++ b/crates/core/src/database/questions.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use oiseau::cache::Cache; use tetratto_shared::unix_epoch_timestamp; +use crate::model::addr::RemoteAddr; use crate::model::communities_permissions::CommunityPermission; use crate::model::uploads::{MediaType, MediaUpload}; use crate::model::{ @@ -368,7 +369,10 @@ impl DataManager { // check for ip block if self - .get_ipblock_by_initiator_receiver(receiver.id, &data.ip) + .get_ipblock_by_initiator_receiver( + receiver.id, + &RemoteAddr::from(data.ip.as_str()), + ) .await .is_ok() { @@ -393,7 +397,7 @@ impl DataManager { // check for ip block if self - .get_ipblock_by_initiator_receiver(receiver.id, &data.ip) + .get_ipblock_by_initiator_receiver(receiver.id, &RemoteAddr::from(data.ip.as_str())) .await .is_ok() { diff --git a/crates/core/src/database/reactions.rs b/crates/core/src/database/reactions.rs index 4d15190..0a61261 100644 --- a/crates/core/src/database/reactions.rs +++ b/crates/core/src/database/reactions.rs @@ -1,5 +1,6 @@ use oiseau::cache::Cache; use crate::model::{ + addr::RemoteAddr, auth::{AchievementName, Notification, User}, permissions::FinePermission, reactions::{AssetType, Reaction}, @@ -127,7 +128,12 @@ impl DataManager { /// /// # Arguments /// * `data` - a mock [`Reaction`] object to insert - pub async fn create_reaction(&self, data: Reaction, user: &User) -> Result<()> { + pub async fn create_reaction( + &self, + data: Reaction, + user: &User, + addr: &RemoteAddr, + ) -> Result<()> { let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), @@ -140,10 +146,14 @@ impl DataManager { .get_userblock_by_initiator_receiver(post.owner, user.id) .await .is_ok() - | self + | (self .get_user_stack_blocked_users(post.owner) .await - .contains(&user.id)) + .contains(&user.id) + | self + .get_ipblock_by_initiator_receiver(post.owner, &addr) + .await + .is_ok())) && !user.permissions.check(FinePermission::MANAGE_POSTS) { return Err(Error::NotAllowed); diff --git a/crates/core/src/database/userblocks.rs b/crates/core/src/database/userblocks.rs index ac95cab..3e9afd5 100644 --- a/crates/core/src/database/userblocks.rs +++ b/crates/core/src/database/userblocks.rs @@ -85,7 +85,18 @@ impl DataManager { } /// Get the receiver of all user blocks for the given `initiator`. - pub async fn get_userblocks_receivers(&self, initiator: usize) -> Vec { + pub async fn get_userblocks_receivers( + &self, + initiator: usize, + associated: &Vec, + ) -> Vec { + let mut associated_str = String::new(); + + for id in associated { + associated_str.push_str(&(" OR initiator = ".to_string() + &id.to_string())); + } + + // ... let conn = match self.0.connect().await { Ok(c) => c, Err(_) => return Vec::new(), @@ -93,7 +104,7 @@ impl DataManager { let res = query_rows!( &conn, - "SELECT * FROM userblocks WHERE initiator = $1", + &format!("SELECT * FROM userblocks WHERE initiator = $1{associated_str}"), &[&(initiator as i64)], |x| { Self::get_userblock_from_row(x) } );