Compare commits

...
6 Commits
9 changed files with 64 additions and 32 deletions

No files matched your search

Generated
+4 -3
View File
@@ -1454,12 +1454,12 @@ checksum = "90f97a5f38dd3ccfbe7aa80f4a0c00930f21b922c74195be0201c51028f22dcf"
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.8.2" version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6012d540c5baa3589337a98ce73408de9b5a25ec9fc2c6fd6be8f0d39e0ca5a" checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [ dependencies = [
"autocfg 1.1.0", "autocfg 1.1.0",
"hashbrown 0.11.2", "hashbrown 0.12.1",
"serde", "serde",
] ]
@@ -2783,6 +2783,7 @@ dependencies = [
"dotenv", "dotenv",
"futures", "futures",
"impl_ops", "impl_ops",
"indexmap",
"iso8601-timestamp", "iso8601-timestamp",
"lazy_static", "lazy_static",
"linkify 0.8.1", "linkify 0.8.1",
@@ -83,6 +83,7 @@ pub async fn req(
} }
// Transfer ownership // Transfer ownership
partial.owner = Some(new_owner.to_string());
let old_owner = std::mem::replace(owner, new_owner.to_string()); let old_owner = std::mem::replace(owner, new_owner.to_string());
// Notify clients // Notify clients
@@ -116,7 +116,7 @@ pub async fn message_send(
} }
// 3. Ensure interactions information is correct // 3. Ensure interactions information is correct
message.interactions.validate(db).await?; message.interactions.validate(db, &mut permissions).await?;
// 4. Verify replies are valid. // 4. Verify replies are valid.
let mut replies = HashSet::new(); let mut replies = HashSet::new();
@@ -55,7 +55,7 @@ pub async fn create_emoji(
// Check that there are no more than 100 emoji // Check that there are no more than 100 emoji
// ! FIXME: hardcoded upper limit // ! FIXME: hardcoded upper limit
let emojis = db.fetch_emoji_by_parent_id(&server.id).await?; let emojis = db.fetch_emoji_by_parent_id(&server.id).await?;
if emojis.len() > 100 { if emojis.len() > 99 {
return Err(Error::TooManyEmoji); return Err(Error::TooManyEmoji);
} }
} }
+29 -13
View File
@@ -1,3 +1,5 @@
use std::collections::HashSet;
use revolt_quark::{ use revolt_quark::{
models::{ models::{
server::{Category, FieldsServer, PartialServer, SystemMessageChannels}, server::{Category, FieldsServer, PartialServer, SystemMessageChannels},
@@ -135,29 +137,43 @@ pub async fn req(
} }
} }
// 2. Apply new icon // 2. Validate changes
let mut unknown_channels = HashSet::new();
if let Some(system_messages) = &partial.system_messages {
unknown_channels = system_messages.clone().into_channel_ids();
}
if let Some(categories) = &partial.categories {
let mut channel_ids = HashSet::new();
for category in categories {
for channel in &category.channels {
if channel_ids.contains(channel) {
return Err(Error::InvalidOperation);
}
channel_ids.insert(channel.to_string());
}
}
unknown_channels.extend(channel_ids);
}
if !db.check_channels_exist(&unknown_channels).await? {
return Err(Error::NotFound);
}
// 3. Apply new icon
if let Some(icon) = icon { if let Some(icon) = icon {
partial.icon = Some(File::use_server_icon(db, &icon, &server.id).await?); partial.icon = Some(File::use_server_icon(db, &icon, &server.id).await?);
server.icon = partial.icon.clone(); server.icon = partial.icon.clone();
} }
// 3. Apply new banner // 4. Apply new banner
if let Some(banner) = banner { if let Some(banner) = banner {
partial.banner = Some(File::use_banner(db, &banner, &server.id).await?); partial.banner = Some(File::use_banner(db, &banner, &server.id).await?);
server.banner = partial.banner.clone(); server.banner = partial.banner.clone();
} }
// 4. Validate changes
if let Some(system_messages) = &partial.system_messages {
let channels = system_messages.clone().into_channel_ids();
if !db
.check_channels_exist(&channels.into_iter().collect())
.await?
{
return Err(Error::NotFound);
}
}
server server
.update(db, partial, remove.unwrap_or_default()) .update(db, partial, remove.unwrap_or_default())
.await?; .await?;
+1
View File
@@ -64,6 +64,7 @@ regex = "1.5.5"
nanoid = "0.4.0" nanoid = "0.4.0"
linkify = "0.8.1" linkify = "0.8.1"
dotenv = "0.15.0" dotenv = "0.15.0"
indexmap = "1.9.1"
impl_ops = "0.1.1" impl_ops = "0.1.1"
num_enum = "0.5.6" num_enum = "0.5.6"
reqwest = "0.11.10" reqwest = "0.11.10"
@@ -13,13 +13,14 @@ use crate::{
}, },
Channel, Emoji, Message, User, Channel, Emoji, Message, User,
}, },
permissions::PermissionCalculator,
presence::presence_filter_online, presence::presence_filter_online,
tasks::ack::AckEvent, tasks::ack::AckEvent,
types::{ types::{
january::{Embed, Text}, january::{Embed, Text},
push::PushNotification, push::PushNotification,
}, },
Database, Error, Result, Database, Error, Permission, Result,
}; };
impl Message { impl Message {
@@ -195,6 +196,11 @@ impl Message {
/// Add a reaction to a message /// Add a reaction to a message
pub async fn add_reaction(&self, db: &Database, user: &User, emoji: &str) -> Result<()> { pub async fn add_reaction(&self, db: &Database, user: &User, emoji: &str) -> Result<()> {
// Check how many reactions are already on the message
if self.reactions.len() >= 20 {
return Err(Error::InvalidOperation);
}
// Check if the emoji is whitelisted // Check if the emoji is whitelisted
if !self.interactions.can_use(emoji) { if !self.interactions.can_use(emoji) {
return Err(Error::InvalidOperation); return Err(Error::InvalidOperation);
@@ -401,8 +407,14 @@ impl BulkMessageResponse {
impl Interactions { impl Interactions {
/// Validate interactions info is correct /// Validate interactions info is correct
pub async fn validate(&self, db: &Database) -> Result<()> { pub async fn validate(
&self,
db: &Database,
permissions: &mut PermissionCalculator<'_>,
) -> Result<()> {
if let Some(reactions) = &self.reactions { if let Some(reactions) = &self.reactions {
permissions.throw_permission(db, Permission::React).await?;
if reactions.len() > 20 { if reactions.len() > 20 {
return Err(Error::InvalidOperation); return Err(Error::InvalidOperation);
} }
@@ -1,3 +1,5 @@
use std::collections::HashSet;
use iso8601_timestamp::Timestamp; use iso8601_timestamp::Timestamp;
use ulid::Ulid; use ulid::Ulid;
@@ -310,23 +312,23 @@ impl Server {
} }
impl SystemMessageChannels { impl SystemMessageChannels {
pub fn into_channel_ids(self) -> Vec<String> { pub fn into_channel_ids(self) -> HashSet<String> {
let mut ids = vec![]; let mut ids = HashSet::new();
if let Some(id) = self.user_joined { if let Some(id) = self.user_joined {
ids.push(id); ids.insert(id);
} }
if let Some(id) = self.user_left { if let Some(id) = self.user_left {
ids.push(id); ids.insert(id);
} }
if let Some(id) = self.user_kicked { if let Some(id) = self.user_kicked {
ids.push(id); ids.insert(id);
} }
if let Some(id) = self.user_banned { if let Some(id) = self.user_banned {
ids.push(id); ids.insert(id);
} }
ids ids
+5 -6
View File
@@ -1,11 +1,10 @@
use crate::util::regex::RE_COLOUR; use crate::util::regex::RE_COLOUR;
use std::collections::{HashMap, HashSet};
use indexmap::{IndexMap, IndexSet};
use iso8601_timestamp::Timestamp;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use validator::Validate; use validator::Validate;
use iso8601_timestamp::Timestamp;
#[cfg(feature = "rocket_impl")] #[cfg(feature = "rocket_impl")]
use rocket::FromFormField; use rocket::FromFormField;
@@ -99,7 +98,7 @@ pub struct Masquerade {
pub struct Interactions { pub struct Interactions {
/// Reactions which should always appear and be distinct /// Reactions which should always appear and be distinct
#[serde(skip_serializing_if = "Option::is_none", default)] #[serde(skip_serializing_if = "Option::is_none", default)]
pub reactions: Option<HashSet<String>>, pub reactions: Option<IndexSet<String>>,
/// Whether reactions should be restricted to the given list /// Whether reactions should be restricted to the given list
#[serde(skip_serializing_if = "if_false", default)] #[serde(skip_serializing_if = "if_false", default)]
pub restrict_reactions: bool, pub restrict_reactions: bool,
@@ -145,8 +144,8 @@ pub struct Message {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub replies: Option<Vec<String>>, pub replies: Option<Vec<String>>,
/// Hashmap of emoji IDs to array of user IDs /// Hashmap of emoji IDs to array of user IDs
#[serde(skip_serializing_if = "HashMap::is_empty", default)] #[serde(skip_serializing_if = "IndexMap::is_empty", default)]
pub reactions: HashMap<String, HashSet<String>>, pub reactions: IndexMap<String, IndexSet<String>>,
/// Information about how this message should be interacted with /// Information about how this message should be interacted with
#[serde(skip_serializing_if = "Interactions::is_default", default)] #[serde(skip_serializing_if = "Interactions::is_default", default)]
pub interactions: Interactions, pub interactions: Interactions,