feat: ratelimit user edit route and discriminator changes
This commit is contained in:
@@ -71,3 +71,14 @@ impl From<Database> for authifier::Database {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Database> for revolt_database::Database {
|
||||
fn from(val: Database) -> Self {
|
||||
match val {
|
||||
Database::Dummy(_) => revolt_database::Database::Reference(Default::default()),
|
||||
Database::MongoDb(MongoDb(client)) => revolt_database::Database::MongoDb(
|
||||
revolt_database::MongoDb(client, "revolt".to_string()),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ use futures::try_join;
|
||||
use impl_ops::impl_op_ex_commutative;
|
||||
use once_cell::sync::Lazy;
|
||||
use rand::seq::SliceRandom;
|
||||
use revolt_database::RatelimitEventType;
|
||||
use revolt_presence::filter_online;
|
||||
use std::collections::HashSet;
|
||||
use std::ops;
|
||||
use std::time::Duration;
|
||||
|
||||
impl_op_ex_commutative!(+ |a: &i32, b: &Badges| -> i32 { *a | *b as i32 });
|
||||
|
||||
@@ -207,7 +209,7 @@ impl User {
|
||||
pub async fn find_discriminator(
|
||||
db: &Database,
|
||||
username: &str,
|
||||
preferred: Option<String>,
|
||||
preferred: Option<(String, String)>,
|
||||
) -> Result<String> {
|
||||
let search_space: &HashSet<String> = &DISCRIMINATOR_SEARCH_SPACE_QUARK;
|
||||
let used_discriminators: HashSet<String> = db
|
||||
@@ -223,9 +225,31 @@ impl User {
|
||||
return Err(Error::UsernameTaken);
|
||||
}
|
||||
|
||||
if let Some(preferred) = preferred {
|
||||
if let Some((preferred, target_id)) = preferred {
|
||||
if available_discriminators.contains(&&preferred) {
|
||||
return Ok(preferred);
|
||||
} else {
|
||||
let rvdb: revolt_database::Database = db.clone().into();
|
||||
if rvdb
|
||||
.has_ratelimited(
|
||||
&target_id,
|
||||
RatelimitEventType::DiscriminatorChange,
|
||||
Duration::from_secs(60 * 60 * 24),
|
||||
1,
|
||||
)
|
||||
.await
|
||||
.map_err(Error::from_core)?
|
||||
{
|
||||
return Err(Error::DiscriminatorChangeRatelimited);
|
||||
}
|
||||
|
||||
rvdb.insert_ratelimit_event(&revolt_database::RatelimitEvent {
|
||||
id: ulid::Ulid::new().to_string(),
|
||||
target_id,
|
||||
event_type: RatelimitEventType::DiscriminatorChange,
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from_core)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -257,7 +281,7 @@ impl User {
|
||||
User::find_discriminator(
|
||||
db,
|
||||
&username,
|
||||
Some(self.discriminator.to_string()),
|
||||
Some((self.discriminator.to_string(), self.id.clone())),
|
||||
)
|
||||
.await?,
|
||||
),
|
||||
|
||||
@@ -31,6 +31,7 @@ pub enum Error {
|
||||
// ? User related errors
|
||||
UsernameTaken,
|
||||
InvalidUsername,
|
||||
DiscriminatorChangeRatelimited,
|
||||
UnknownUser,
|
||||
AlreadyFriends,
|
||||
AlreadySentRequest,
|
||||
@@ -165,6 +166,7 @@ impl<'r> Responder<'r, 'static> for Error {
|
||||
|
||||
Error::UnknownUser => Status::NotFound,
|
||||
Error::InvalidUsername => Status::BadRequest,
|
||||
Error::DiscriminatorChangeRatelimited => Status::TooManyRequests,
|
||||
Error::UsernameTaken => Status::Conflict,
|
||||
Error::AlreadyFriends => Status::Conflict,
|
||||
Error::AlreadySentRequest => Status::Conflict,
|
||||
|
||||
@@ -101,16 +101,19 @@ pub struct Ratelimiter {
|
||||
fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r str>) {
|
||||
if let Some(segment) = request.routed_segment(0) {
|
||||
let resource = request.routed_segment(1);
|
||||
match (segment, resource) {
|
||||
("users", _) => {
|
||||
|
||||
let method = request.method();
|
||||
match (segment, resource, method) {
|
||||
("users", target, Method::Patch) => ("user_edit", target),
|
||||
("users", _, _) => {
|
||||
if let Some("default_avatar") = request.routed_segment(2) {
|
||||
return ("default_avatar", None);
|
||||
}
|
||||
|
||||
("users", None)
|
||||
}
|
||||
("bots", _) => ("bots", None),
|
||||
("channels", Some(id)) => {
|
||||
("bots", _, _) => ("bots", None),
|
||||
("channels", Some(id), _) => {
|
||||
if request.method() == Method::Post {
|
||||
if let Some("messages") = request.routed_segment(2) {
|
||||
return ("messaging", Some(id));
|
||||
@@ -119,17 +122,17 @@ fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r
|
||||
|
||||
("channels", Some(id))
|
||||
}
|
||||
("servers", Some(id)) => ("servers", Some(id)),
|
||||
("auth", _) => {
|
||||
("servers", Some(id), _) => ("servers", Some(id)),
|
||||
("auth", _, _) => {
|
||||
if request.method() == Method::Delete {
|
||||
("auth_delete", None)
|
||||
} else {
|
||||
("auth", None)
|
||||
}
|
||||
}
|
||||
("swagger", _) => ("swagger", None),
|
||||
("safety", Some("report")) => ("safety_report", Some("report")),
|
||||
("safety", _) => ("safety", None),
|
||||
("swagger", _, _) => ("swagger", None),
|
||||
("safety", Some("report"), _) => ("safety_report", Some("report")),
|
||||
("safety", _, _) => ("safety", None),
|
||||
_ => ("any", None),
|
||||
}
|
||||
} else {
|
||||
@@ -140,6 +143,7 @@ fn resolve_bucket<'r>(request: &'r rocket::Request<'_>) -> (&'r str, Option<&'r
|
||||
/// Resolve per-bucket limits
|
||||
fn resolve_bucket_limit(bucket: &str) -> u8 {
|
||||
match bucket {
|
||||
"user_edit" => 2,
|
||||
"users" => 20,
|
||||
"bots" => 10,
|
||||
"messaging" => 10,
|
||||
|
||||
Reference in New Issue
Block a user