feat: add webhook permissions
This commit is contained in:
@@ -9,6 +9,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use revolt_permissions::DEFAULT_WEBHOOK_PERMISSIONS;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
@@ -945,6 +946,23 @@ pub async fn run_migrations(db: &MongoDb, revision: i32) -> i32 {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create username index.");
|
.expect("Failed to create username index.");
|
||||||
|
};
|
||||||
|
|
||||||
|
if revision <= 25 {
|
||||||
|
info!("Running migration [revision 25 / 11-06-2023]: Add permissions to webhooks.");
|
||||||
|
|
||||||
|
db.col::<Document>("webhooks")
|
||||||
|
.update_many(
|
||||||
|
doc! {},
|
||||||
|
doc! {
|
||||||
|
"$set": {
|
||||||
|
"permissions": *DEFAULT_WEBHOOK_PERMISSIONS as i64
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("Failed to update webhooks.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if revision <= 25 {
|
if revision <= 25 {
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ auto_derived_partial!(
|
|||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
|
/// The permissions of the webhook
|
||||||
|
pub permissions: u64,
|
||||||
|
|
||||||
/// The private token for the webhook
|
/// The private token for the webhook
|
||||||
pub token: Option<String>,
|
pub token: Option<String>,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ impl From<crate::Webhook> for Webhook {
|
|||||||
avatar: value.avatar.map(|file| file.into()),
|
avatar: value.avatar.map(|file| file.into()),
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
token: value.token,
|
token: value.token,
|
||||||
|
permissions: value.permissions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -64,6 +65,7 @@ impl From<crate::PartialWebhook> for PartialWebhook {
|
|||||||
avatar: value.avatar.map(|file| file.into()),
|
avatar: value.avatar.map(|file| file.into()),
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
token: value.token,
|
token: value.token,
|
||||||
|
permissions: value.permissions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ auto_derived_partial!(
|
|||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
|
/// The permissions for the webhook
|
||||||
|
pub permissions: u64,
|
||||||
|
|
||||||
/// The private token for the webhook
|
/// The private token for the webhook
|
||||||
pub token: Option<String>,
|
pub token: Option<String>,
|
||||||
},
|
},
|
||||||
@@ -43,6 +46,9 @@ auto_derived!(
|
|||||||
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
|
#[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))]
|
||||||
pub avatar: Option<String>,
|
pub avatar: Option<String>,
|
||||||
|
|
||||||
|
/// Webhook permissions
|
||||||
|
pub permissions: Option<u64>,
|
||||||
|
|
||||||
/// Fields to remove from webhook
|
/// Fields to remove from webhook
|
||||||
#[cfg_attr(feature = "serde", serde(default))]
|
#[cfg_attr(feature = "serde", serde(default))]
|
||||||
pub remove: Vec<FieldsWebhook>,
|
pub remove: Vec<FieldsWebhook>,
|
||||||
@@ -61,6 +67,9 @@ auto_derived!(
|
|||||||
|
|
||||||
/// The channel this webhook belongs to
|
/// The channel this webhook belongs to
|
||||||
pub channel_id: String,
|
pub channel_id: String,
|
||||||
|
|
||||||
|
/// The permissions for the webhook
|
||||||
|
pub permissions: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional fields on webhook object
|
/// Optional fields on webhook object
|
||||||
@@ -85,6 +94,7 @@ impl From<Webhook> for ResponseWebhook {
|
|||||||
name: value.name,
|
name: value.name,
|
||||||
avatar: value.avatar.map(|file| file.id),
|
avatar: value.avatar.map(|file| file.id),
|
||||||
channel_id: value.channel_id,
|
channel_id: value.channel_id,
|
||||||
|
permissions: value.permissions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,3 +135,5 @@ pub static DEFAULT_PERMISSION_SERVER: Lazy<u64> = Lazy::new(|| {
|
|||||||
+ ChannelPermission::ChangeAvatar,
|
+ ChannelPermission::ChangeAvatar,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pub static DEFAULT_WEBHOOK_PERMISSIONS: Lazy<u64> = Lazy::new(|| ChannelPermission::SendMessage + ChannelPermission::SendEmbeds + ChannelPermission::Masquerade + ChannelPermission::React);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use revolt_database::{Database, Webhook};
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::{Channel, User},
|
models::{Channel, User},
|
||||||
perms, Db, Error, Permission, Ref, Result,
|
perms, Db, Error, Permission, Ref, Result,
|
||||||
|
DEFAULT_WEBHOOK_PERMISSIONS,
|
||||||
};
|
};
|
||||||
use rocket::{serde::json::Json, State};
|
use rocket::{serde::json::Json, State};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -60,6 +61,7 @@ pub async fn req(
|
|||||||
name: data.name,
|
name: data.name,
|
||||||
avatar,
|
avatar,
|
||||||
channel_id: channel.id().to_string(),
|
channel_id: channel.id().to_string(),
|
||||||
|
permissions: *DEFAULT_WEBHOOK_PERMISSIONS,
|
||||||
token: Some(nanoid::nanoid!(64)),
|
token: Some(nanoid::nanoid!(64)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -35,11 +35,13 @@ pub async fn webhook_edit(
|
|||||||
let DataEditWebhook {
|
let DataEditWebhook {
|
||||||
name,
|
name,
|
||||||
avatar,
|
avatar,
|
||||||
|
permissions,
|
||||||
remove,
|
remove,
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let mut partial = PartialWebhook {
|
let mut partial = PartialWebhook {
|
||||||
name,
|
name,
|
||||||
|
permissions,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,13 @@ pub async fn webhook_edit_token(
|
|||||||
let DataEditWebhook {
|
let DataEditWebhook {
|
||||||
name,
|
name,
|
||||||
avatar,
|
avatar,
|
||||||
remove,
|
permissions,
|
||||||
|
remove
|
||||||
} = data;
|
} = data;
|
||||||
|
|
||||||
let mut partial = PartialWebhook {
|
let mut partial = PartialWebhook {
|
||||||
name,
|
name,
|
||||||
|
permissions,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ pub async fn webhook_execute(
|
|||||||
let webhook = webhook_id.as_webhook(db).await.map_err(Error::from_core)?;
|
let webhook = webhook_id.as_webhook(db).await.map_err(Error::from_core)?;
|
||||||
webhook.assert_token(&token).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)
|
data.validate_webhook_permissions(webhook.permissions)?;
|
||||||
// TODO: they can also mention anyone
|
|
||||||
|
|
||||||
let channel = legacy_db.fetch_channel(&webhook.channel_id).await?;
|
let channel = legacy_db.fetch_channel(&webhook.channel_id).await?;
|
||||||
let message = channel
|
let message = channel
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::{
|
|||||||
models::{
|
models::{
|
||||||
message::{
|
message::{
|
||||||
AppendMessage, BulkMessageResponse, Interactions, PartialMessage, SendableEmbed,
|
AppendMessage, BulkMessageResponse, Interactions, PartialMessage, SendableEmbed,
|
||||||
SystemMessage,
|
SystemMessage, DataMessageSend,
|
||||||
},
|
},
|
||||||
Channel, Emoji, Message, User,
|
Channel, Emoji, Message, User,
|
||||||
},
|
},
|
||||||
@@ -451,3 +451,39 @@ impl Interactions {
|
|||||||
!self.restrict_reactions && self.reactions.is_none()
|
!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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ pub static DEFAULT_PERMISSION: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION_VIEW_
|
|||||||
pub static DEFAULT_PERMISSION_SAVED_MESSAGES: u64 = Permission::GrantAllSafe as u64;
|
pub static DEFAULT_PERMISSION_SAVED_MESSAGES: u64 = Permission::GrantAllSafe as u64;
|
||||||
pub static DEFAULT_PERMISSION_DIRECT_MESSAGE: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::ManageChannel + Permission::React));
|
pub static DEFAULT_PERMISSION_DIRECT_MESSAGE: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::ManageChannel + Permission::React));
|
||||||
pub static DEFAULT_PERMISSION_SERVER: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::React + Permission::ChangeNickname + Permission::ChangeAvatar));
|
pub static DEFAULT_PERMISSION_SERVER: Lazy<u64> = Lazy::new(|| DEFAULT_PERMISSION.add(Permission::React + Permission::ChangeNickname + Permission::ChangeAvatar));
|
||||||
|
pub static DEFAULT_WEBHOOK_PERMISSIONS: Lazy<u64> = Lazy::new(|| Permission::SendMessage + Permission::SendEmbeds + Permission::Masquerade + Permission::React);
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|||||||
Reference in New Issue
Block a user