add: use RemoteAddr for ip blocks as well

This commit is contained in:
trisua 2025-06-30 15:35:18 -04:00
parent 14936b8b90
commit f5faed7762
12 changed files with 127 additions and 28 deletions

View file

@ -352,7 +352,10 @@ macro_rules! ignore_users_gen {
($user:ident, $data:ident) => { ($user:ident, $data:ident) => {
if let Some(ref ua) = $user { 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_userblocks_initiator_by_receivers(ua.id).await,
$data.0.get_user_stack_blocked_users(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) => {{ ($user:ident!, $data:ident) => {{
[ [
$data.0.get_userblocks_receivers($user.id).await, $data
.0
.get_userblocks_receivers($user.id, &$user.associated)
.await,
$data $data
.0 .0
.get_userblocks_initiator_by_receivers($user.id) .get_userblocks_initiator_by_receivers($user.id)
@ -376,7 +382,9 @@ macro_rules! ignore_users_gen {
($user:ident!, #$data:ident) => { ($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, $data.get_userblocks_initiator_by_receivers($user.id).await,
] ]
.concat() .concat()

View file

@ -54,7 +54,7 @@ pub async fn register_request(
// check for ip ban // check for ip ban
if data if data
.get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str()))
.await .await
.is_ok() .is_ok()
{ {
@ -189,7 +189,7 @@ pub async fn login_request(
// check for ip ban // check for ip ban
if data if data
.get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str()))
.await .await
.is_ok() .is_ok()
{ {

View file

@ -11,6 +11,7 @@ use axum::{
}; };
use axum_extra::extract::CookieJar; use axum_extra::extract::CookieJar;
use tetratto_core::model::{ use tetratto_core::model::{
addr::RemoteAddr,
auth::{AchievementName, FollowResult, IpBlock, Notification, UserBlock, UserFollow}, auth::{AchievementName, FollowResult, IpBlock, Notification, UserBlock, UserFollow},
oauth, oauth,
}; };
@ -228,7 +229,10 @@ pub async fn ip_block_request(
None => return Json(Error::NotAllowed.into()), 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 // delete
match data.delete_ipblock(ipblock.id, user).await { match data.delete_ipblock(ipblock.id, user).await {
Ok(_) => Json(ApiReturn { Ok(_) => Json(ApiReturn {
@ -335,7 +339,7 @@ pub async fn ip_block_profile_request(
for (ip, _, _) in other_user.tokens { for (ip, _, _) in other_user.tokens {
// check for an existing ip block // check for an existing ip block
if data if data
.get_ipblock_by_initiator_receiver(user.id, &ip) .get_ipblock_by_initiator_receiver(user.id, &RemoteAddr::from(ip.as_str()))
.await .await
.is_ok() .is_ok()
{ {

View file

@ -67,7 +67,7 @@ pub async fn create_request(
// check for ip ban // check for ip ban
if data if data
.get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str()))
.await .await
.is_ok() .is_ok()
{ {

View file

@ -43,7 +43,7 @@ pub async fn create_request(
// check for ip ban // check for ip ban
if data if data
.get_ipban_by_addr(RemoteAddr::from(real_ip.as_str())) .get_ipban_by_addr(&RemoteAddr::from(real_ip.as_str()))
.await .await
.is_ok() .is_ok()
{ {
@ -145,7 +145,7 @@ pub async fn ip_block_request(
// check for an existing ip block // check for an existing ip block
if data 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 .await
.is_ok() .is_ok()
{ {

View file

@ -1,7 +1,12 @@
use crate::{State, get_user_from_token, routes::api::v1::CreateReaction}; 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 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( pub async fn get_request(
jar: CookieJar, jar: CookieJar,
@ -26,6 +31,7 @@ pub async fn get_request(
pub async fn create_request( pub async fn create_request(
jar: CookieJar, jar: CookieJar,
headers: HeaderMap,
Extension(data): Extension<State>, Extension(data): Extension<State>,
Json(req): Json<CreateReaction>, Json(req): Json<CreateReaction>,
) -> impl IntoResponse { ) -> impl IntoResponse {
@ -40,6 +46,20 @@ pub async fn create_request(
Err(e) => return Json(Error::MiscError(e.to_string()).into()), 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 // check for existing reaction
if let Ok(r) = data.get_reaction_by_owner_asset(user.id, asset_id).await { if let Ok(r) = data.get_reaction_by_owner_asset(user.id, asset_id).await {
match data.delete_reaction(r.id, &user).await { match data.delete_reaction(r.id, &user).await {
@ -63,6 +83,7 @@ pub async fn create_request(
.create_reaction( .create_reaction(
Reaction::new(user.id, asset_id, req.asset_type, req.is_like), Reaction::new(user.id, asset_id, req.asset_type, req.is_like),
&user, &user,
&addr,
) )
.await .await
{ {

View file

@ -3,14 +3,16 @@ use crate::{
assets::initial_context, check_user_blocked_or_private, get_lang, get_user_from_token, State, assets::initial_context, check_user_blocked_or_private, get_lang, get_user_from_token, State,
}; };
use axum::{ use axum::{
Extension,
extract::{Path, Query}, extract::{Path, Query},
http::{HeaderMap, HeaderValue},
response::{Html, IntoResponse}, response::{Html, IntoResponse},
Extension,
}; };
use axum_extra::extract::CookieJar; use axum_extra::extract::CookieJar;
use serde::Deserialize; use serde::Deserialize;
use tera::Context; use tera::Context;
use tetratto_core::model::{ use tetratto_core::model::{
addr::RemoteAddr,
auth::User, auth::User,
communities::Community, communities::Community,
communities_permissions::CommunityPermission, communities_permissions::CommunityPermission,
@ -642,6 +644,7 @@ pub async fn settings_request(
/// `/post/{id}` /// `/post/{id}`
pub async fn post_request( pub async fn post_request(
jar: CookieJar, jar: CookieJar,
headers: HeaderMap,
Path(id): Path<usize>, Path(id): Path<usize>,
Query(props): Query<PaginatedQuery>, Query(props): Query<PaginatedQuery>,
Extension(data): Extension<State>, Extension(data): Extension<State>,
@ -751,6 +754,46 @@ pub async fn post_request(
check_user_blocked_or_private!(user, owner, data, jar); 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 // check repost
let (_, reposting) = data.0.get_post_reposting(&post, &ignore_users, &user).await; let (_, reposting) = data.0.get_post_reposting(&post, &ignore_users, &user).await;

View file

@ -23,7 +23,7 @@ impl DataManager {
/// ///
/// # Arguments /// # Arguments
/// * `prefix` /// * `prefix`
pub async fn get_ipban_by_addr(&self, addr: RemoteAddr) -> Result<IpBan> { pub async fn get_ipban_by_addr(&self, addr: &RemoteAddr) -> Result<IpBan> {
let conn = match self.0.connect().await { let conn = match self.0.connect().await {
Ok(c) => c, Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())), Err(e) => return Err(Error::DatabaseConnection(e.to_string())),

View file

@ -1,10 +1,8 @@
use oiseau::cache::Cache; use oiseau::cache::Cache;
use crate::model::addr::RemoteAddr;
use crate::model::{Error, Result, auth::User, auth::IpBlock, permissions::FinePermission}; use crate::model::{Error, Result, auth::User, auth::IpBlock, permissions::FinePermission};
use crate::{auto_method, DataManager}; use crate::{auto_method, DataManager};
use oiseau::{query_rows, PostgresRow, execute, get, query_row, params};
use oiseau::{query_rows, PostgresRow};
use oiseau::{execute, get, query_row, params};
impl DataManager { impl DataManager {
/// Get an [`IpBlock`] from an SQL row. /// Get an [`IpBlock`] from an SQL row.
@ -23,7 +21,7 @@ impl DataManager {
pub async fn get_ipblock_by_initiator_receiver( pub async fn get_ipblock_by_initiator_receiver(
&self, &self,
initiator: usize, initiator: usize,
receiver: &str, receiver: &RemoteAddr,
) -> Result<IpBlock> { ) -> Result<IpBlock> {
let conn = match self.0.connect().await { let conn = match self.0.connect().await {
Ok(c) => c, Ok(c) => c,
@ -32,8 +30,8 @@ impl DataManager {
let res = query_row!( let res = query_row!(
&conn, &conn,
"SELECT * FROM ipblocks WHERE initiator = $1 AND receiver = $2", "SELECT * FROM ipblocks WHERE initiator = $1 AND receiver LIKE $2",
params![&(initiator as i64), &receiver], params![&(initiator as i64), &format!("{}%", receiver.prefix(None))],
|x| { Ok(Self::get_ipblock_from_row(x)) } |x| { Ok(Self::get_ipblock_from_row(x)) }
); );

View file

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use oiseau::cache::Cache; use oiseau::cache::Cache;
use tetratto_shared::unix_epoch_timestamp; use tetratto_shared::unix_epoch_timestamp;
use crate::model::addr::RemoteAddr;
use crate::model::communities_permissions::CommunityPermission; use crate::model::communities_permissions::CommunityPermission;
use crate::model::uploads::{MediaType, MediaUpload}; use crate::model::uploads::{MediaType, MediaUpload};
use crate::model::{ use crate::model::{
@ -368,7 +369,10 @@ impl DataManager {
// check for ip block // check for ip block
if self 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 .await
.is_ok() .is_ok()
{ {
@ -393,7 +397,7 @@ impl DataManager {
// check for ip block // check for ip block
if self 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 .await
.is_ok() .is_ok()
{ {

View file

@ -1,5 +1,6 @@
use oiseau::cache::Cache; use oiseau::cache::Cache;
use crate::model::{ use crate::model::{
addr::RemoteAddr,
auth::{AchievementName, Notification, User}, auth::{AchievementName, Notification, User},
permissions::FinePermission, permissions::FinePermission,
reactions::{AssetType, Reaction}, reactions::{AssetType, Reaction},
@ -127,7 +128,12 @@ impl DataManager {
/// ///
/// # Arguments /// # Arguments
/// * `data` - a mock [`Reaction`] object to insert /// * `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 { let conn = match self.0.connect().await {
Ok(c) => c, Ok(c) => c,
Err(e) => return Err(Error::DatabaseConnection(e.to_string())), Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
@ -140,10 +146,14 @@ impl DataManager {
.get_userblock_by_initiator_receiver(post.owner, user.id) .get_userblock_by_initiator_receiver(post.owner, user.id)
.await .await
.is_ok() .is_ok()
| self | (self
.get_user_stack_blocked_users(post.owner) .get_user_stack_blocked_users(post.owner)
.await .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) && !user.permissions.check(FinePermission::MANAGE_POSTS)
{ {
return Err(Error::NotAllowed); return Err(Error::NotAllowed);

View file

@ -85,7 +85,18 @@ impl DataManager {
} }
/// Get the receiver of all user blocks for the given `initiator`. /// Get the receiver of all user blocks for the given `initiator`.
pub async fn get_userblocks_receivers(&self, initiator: usize) -> Vec<usize> { pub async fn get_userblocks_receivers(
&self,
initiator: usize,
associated: &Vec<usize>,
) -> Vec<usize> {
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 { let conn = match self.0.connect().await {
Ok(c) => c, Ok(c) => c,
Err(_) => return Vec::new(), Err(_) => return Vec::new(),
@ -93,7 +104,7 @@ impl DataManager {
let res = query_rows!( let res = query_rows!(
&conn, &conn,
"SELECT * FROM userblocks WHERE initiator = $1", &format!("SELECT * FROM userblocks WHERE initiator = $1{associated_str}"),
&[&(initiator as i64)], &[&(initiator as i64)],
|x| { Self::get_userblock_from_row(x) } |x| { Self::get_userblock_from_row(x) }
); );