From 54878e8e8dc2240463535093e7f4a793a3f68855 Mon Sep 17 00:00:00 2001 From: Zomatree Date: Sun, 11 Jun 2023 23:02:53 +0100 Subject: [PATCH] feat: add webhook permissions --- .../admin_migrations/ops/mongodb/scripts.rs | 18 +++++++++ .../src/models/channel_webhooks/model.rs | 3 ++ crates/core/database/src/util/bridge/v0.rs | 2 + crates/core/models/src/v0/channel_webhooks.rs | 10 +++++ crates/core/permissions/src/models/channel.rs | 2 + .../src/routes/channels/webhook_create.rs | 2 + .../delta/src/routes/webhooks/webhook_edit.rs | 2 + .../src/routes/webhooks/webhook_edit_token.rs | 4 +- .../src/routes/webhooks/webhook_execute.rs | 3 +- .../src/impl/generic/channels/message.rs | 38 ++++++++++++++++++- .../quark/src/permissions/defn/permission.rs | 1 + 11 files changed, 81 insertions(+), 4 deletions(-) diff --git a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs index 615283a4..54a9934f 100644 --- a/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs +++ b/crates/core/database/src/models/admin_migrations/ops/mongodb/scripts.rs @@ -9,6 +9,7 @@ use crate::{ }; use futures::StreamExt; use rand::seq::SliceRandom; +use revolt_permissions::DEFAULT_WEBHOOK_PERMISSIONS; use serde::{Deserialize, Serialize}; use unicode_segmentation::UnicodeSegmentation; @@ -945,6 +946,23 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 { ) .await .expect("Failed to create username index."); + }; + + if revision <= 25 { + info!("Running migration [revision 25 / 11-06-2023]: Add permissions to webhooks."); + + db.col::("webhooks") + .update_many( + doc! {}, + doc! { + "$set": { + "permissions": *DEFAULT_WEBHOOK_PERMISSIONS as i64 + } + }, + None, + ) + .await + .expect("Failed to update webhooks."); } if revision <= 25 { diff --git a/crates/core/database/src/models/channel_webhooks/model.rs b/crates/core/database/src/models/channel_webhooks/model.rs index dba91ff8..87ea23d0 100644 --- a/crates/core/database/src/models/channel_webhooks/model.rs +++ b/crates/core/database/src/models/channel_webhooks/model.rs @@ -20,6 +20,9 @@ auto_derived_partial!( /// The channel this webhook belongs to pub channel_id: String, + /// The permissions of the webhook + pub permissions: u64, + /// The private token for the webhook pub token: Option, }, diff --git a/crates/core/database/src/util/bridge/v0.rs b/crates/core/database/src/util/bridge/v0.rs index 45fad903..eb4180b2 100644 --- a/crates/core/database/src/util/bridge/v0.rs +++ b/crates/core/database/src/util/bridge/v0.rs @@ -52,6 +52,7 @@ impl From for Webhook { avatar: value.avatar.map(|file| file.into()), channel_id: value.channel_id, token: value.token, + permissions: value.permissions } } } @@ -64,6 +65,7 @@ impl From for PartialWebhook { avatar: value.avatar.map(|file| file.into()), channel_id: value.channel_id, token: value.token, + permissions: value.permissions } } } diff --git a/crates/core/models/src/v0/channel_webhooks.rs b/crates/core/models/src/v0/channel_webhooks.rs index 93f5649b..8e7ae042 100644 --- a/crates/core/models/src/v0/channel_webhooks.rs +++ b/crates/core/models/src/v0/channel_webhooks.rs @@ -16,6 +16,9 @@ auto_derived_partial!( /// The channel this webhook belongs to pub channel_id: String, + /// The permissions for the webhook + pub permissions: u64, + /// The private token for the webhook pub token: Option, }, @@ -43,6 +46,9 @@ auto_derived!( #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))] pub avatar: Option, + /// Webhook permissions + pub permissions: Option, + /// Fields to remove from webhook #[cfg_attr(feature = "serde", serde(default))] pub remove: Vec, @@ -61,6 +67,9 @@ auto_derived!( /// The channel this webhook belongs to pub channel_id: String, + + /// The permissions for the webhook + pub permissions: u64 } /// Optional fields on webhook object @@ -85,6 +94,7 @@ impl From for ResponseWebhook { name: value.name, avatar: value.avatar.map(|file| file.id), channel_id: value.channel_id, + permissions: value.permissions } } } diff --git a/crates/core/permissions/src/models/channel.rs b/crates/core/permissions/src/models/channel.rs index 25dd81db..d0aa2ecc 100644 --- a/crates/core/permissions/src/models/channel.rs +++ b/crates/core/permissions/src/models/channel.rs @@ -135,3 +135,5 @@ pub static DEFAULT_PERMISSION_SERVER: Lazy = Lazy::new(|| { + ChannelPermission::ChangeAvatar, ) }); + +pub static DEFAULT_WEBHOOK_PERMISSIONS: Lazy = Lazy::new(|| ChannelPermission::SendMessage + ChannelPermission::SendEmbeds + ChannelPermission::Masquerade + ChannelPermission::React); diff --git a/crates/delta/src/routes/channels/webhook_create.rs b/crates/delta/src/routes/channels/webhook_create.rs index eb8b71bb..4956c675 100644 --- a/crates/delta/src/routes/channels/webhook_create.rs +++ b/crates/delta/src/routes/channels/webhook_create.rs @@ -2,6 +2,7 @@ use revolt_database::{Database, Webhook}; use revolt_quark::{ models::{Channel, User}, perms, Db, Error, Permission, Ref, Result, + DEFAULT_WEBHOOK_PERMISSIONS, }; use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; @@ -60,6 +61,7 @@ pub async fn req( name: data.name, avatar, channel_id: channel.id().to_string(), + permissions: *DEFAULT_WEBHOOK_PERMISSIONS, token: Some(nanoid::nanoid!(64)), }; diff --git a/crates/delta/src/routes/webhooks/webhook_edit.rs b/crates/delta/src/routes/webhooks/webhook_edit.rs index 2d50a891..e1769e8e 100644 --- a/crates/delta/src/routes/webhooks/webhook_edit.rs +++ b/crates/delta/src/routes/webhooks/webhook_edit.rs @@ -35,11 +35,13 @@ pub async fn webhook_edit( let DataEditWebhook { name, avatar, + permissions, remove, } = data; let mut partial = PartialWebhook { name, + permissions, ..Default::default() }; diff --git a/crates/delta/src/routes/webhooks/webhook_edit_token.rs b/crates/delta/src/routes/webhooks/webhook_edit_token.rs index 18250f6f..e60ce6b7 100644 --- a/crates/delta/src/routes/webhooks/webhook_edit_token.rs +++ b/crates/delta/src/routes/webhooks/webhook_edit_token.rs @@ -33,11 +33,13 @@ pub async fn webhook_edit_token( let DataEditWebhook { name, avatar, - remove, + permissions, + remove } = data; let mut partial = PartialWebhook { name, + permissions, ..Default::default() }; diff --git a/crates/delta/src/routes/webhooks/webhook_execute.rs b/crates/delta/src/routes/webhooks/webhook_execute.rs index b79503ab..0ec30299 100644 --- a/crates/delta/src/routes/webhooks/webhook_execute.rs +++ b/crates/delta/src/routes/webhooks/webhook_execute.rs @@ -29,8 +29,7 @@ pub async fn webhook_execute( let webhook = webhook_id.as_webhook(db).await.map_err(Error::from_core)?; webhook.assert_token(&token).map_err(Error::from_core)?; - // TODO: webhooks can currently always send masquerades, files, embeds, reactions (interactions) - // TODO: they can also mention anyone + data.validate_webhook_permissions(webhook.permissions)?; let channel = legacy_db.fetch_channel(&webhook.channel_id).await?; let message = channel diff --git a/crates/quark/src/impl/generic/channels/message.rs b/crates/quark/src/impl/generic/channels/message.rs index f92279bb..937b7c21 100644 --- a/crates/quark/src/impl/generic/channels/message.rs +++ b/crates/quark/src/impl/generic/channels/message.rs @@ -10,7 +10,7 @@ use crate::{ models::{ message::{ AppendMessage, BulkMessageResponse, Interactions, PartialMessage, SendableEmbed, - SystemMessage, + SystemMessage, DataMessageSend, }, Channel, Emoji, Message, User, }, @@ -451,3 +451,39 @@ impl Interactions { !self.restrict_reactions && self.reactions.is_none() } } + + +fn throw_permission(permissions: u64, permission: Permission) -> Result<()> { + if (permission as u64) & permissions == (permission as u64) { + Ok(()) + } else { + Err(Error::MissingPermission { permission }) + } +} + +impl DataMessageSend { + pub fn validate_webhook_permissions( + &self, + permissions: u64, + ) -> Result<()> { + throw_permission(permissions, Permission::SendMessage)?; + + if self.attachments.as_ref().map_or(false, |v| !v.is_empty()) { + throw_permission(permissions, Permission::UploadFiles)?; + }; + + if self.embeds.as_ref().map_or(false, |v| !v.is_empty()) { + throw_permission(permissions, Permission::SendEmbeds)?; + }; + + if self.masquerade.is_some() { + throw_permission(permissions, Permission::Masquerade)?; + }; + + if self.interactions.is_some() { + throw_permission(permissions, Permission::React)?; + }; + + Ok(()) + } +} diff --git a/crates/quark/src/permissions/defn/permission.rs b/crates/quark/src/permissions/defn/permission.rs index a73c6216..144cadd1 100644 --- a/crates/quark/src/permissions/defn/permission.rs +++ b/crates/quark/src/permissions/defn/permission.rs @@ -110,6 +110,7 @@ pub static DEFAULT_PERMISSION: Lazy = Lazy::new(|| DEFAULT_PERMISSION_VIEW_ pub static DEFAULT_PERMISSION_SAVED_MESSAGES: u64 = Permission::GrantAllSafe as u64; pub static DEFAULT_PERMISSION_DIRECT_MESSAGE: Lazy = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::ManageChannel + Permission::React)); pub static DEFAULT_PERMISSION_SERVER: Lazy = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::React + Permission::ChangeNickname + Permission::ChangeAvatar)); +pub static DEFAULT_WEBHOOK_PERMISSIONS: Lazy = Lazy::new(|| Permission::SendMessage + Permission::SendEmbeds + Permission::Masquerade + Permission::React); bitfield! { #[derive(Default)]