From 782bfb7e03eb90bce927236a6b475bddafdde2bd Mon Sep 17 00:00:00 2001 From: Paul <paulmakles@gmail.com> Date: Sun, 6 Jun 2021 15:22:23 +0100 Subject: [PATCH] Servers: Route for kicking a member. --- src/database/entities/server.rs | 26 ++++++++++++++++++++++++++ src/database/entities/user.rs | 10 +++++----- src/database/permissions/server.rs | 5 ++++- src/notifications/events.rs | 29 +++++++++++++++++++---------- src/notifications/hive.rs | 7 +++++-- src/routes/servers/member_remove.rs | 25 +++++++++++++++++++++++++ src/routes/servers/mod.rs | 2 ++ 7 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 src/routes/servers/member_remove.rs diff --git a/src/database/entities/server.rs b/src/database/entities/server.rs index cdea974..4fad955 100644 --- a/src/database/entities/server.rs +++ b/src/database/entities/server.rs @@ -258,4 +258,30 @@ impl Server { Ok(()) } + + pub async fn remove_member(&self, id: &str) -> Result<()> { + get_collection("server_members") + .delete_one( + doc! { + "_id": { + "server": &self.id, + "user": &id + } + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "delete_one", + with: "server_members", + })?; + + ClientboundNotification::ServerMemberLeave { + id: self.id.clone(), + user: id.to_string() + } + .publish(self.id.clone()); + + Ok(()) + } } diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs index 21c427d..6ffd106 100644 --- a/src/database/entities/user.rs +++ b/src/database/entities/user.rs @@ -28,14 +28,14 @@ pub enum RelationshipStatus { BlockedOther, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Relationship { #[serde(rename = "_id")] pub id: String, pub status: RelationshipStatus, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum Presence { Online, Idle, @@ -43,7 +43,7 @@ pub enum Presence { Invisible, } -#[derive(Validate, Serialize, Deserialize, Debug)] +#[derive(Validate, Serialize, Deserialize, Debug, Clone)] pub struct UserStatus { #[validate(length(min = 1, max = 128))] #[serde(skip_serializing_if = "Option::is_none")] @@ -52,7 +52,7 @@ pub struct UserStatus { pub presence: Option<Presence>, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct UserProfile { #[serde(skip_serializing_if = "Option::is_none")] pub content: Option<String>, @@ -72,7 +72,7 @@ pub enum Badges { impl_op_ex_commutative!(+ |a: &i32, b: &Badges| -> i32 { *a | *b as i32 }); // When changing this struct, update notifications/payload.rs#80 -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct User { #[serde(rename = "_id")] pub id: String, diff --git a/src/database/permissions/server.rs b/src/database/permissions/server.rs index 2e73aee..276e248 100644 --- a/src/database/permissions/server.rs +++ b/src/database/permissions/server.rs @@ -9,7 +9,8 @@ use std::ops; #[repr(u32)] pub enum ServerPermission { View = 0b00000000000000000000000000000001, // 1 - // 2 bits of space + ManageMembers = 0b00000000000000000000000000000010, // 2 + ManageChannels = 0b00000000000000000000000000000100, // 4 ManageServer = 0b00000000000000000000000000001000, // 8 // 8 bits of space ChangeNickname = 0b00000000000000000001000000000000, // 4096 @@ -26,6 +27,8 @@ bitfield! { pub struct ServerPermissions(MSB0 [u32]); u32; pub get_view, _: 31; + pub get_manage_members, _: 30; + pub get_manage_channels, _: 29; pub get_manage_server, _: 28; pub get_change_nickname, _: 19; diff --git a/src/notifications/events.rs b/src/notifications/events.rs index 4d7ec25..52fee13 100644 --- a/src/notifications/events.rs +++ b/src/notifications/events.rs @@ -11,7 +11,7 @@ use crate::{ util::result::{Error, Result}, }; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(tag = "error")] pub enum WebSocketError { LabelMe, @@ -29,7 +29,7 @@ pub enum ServerboundNotification { EndTyping { channel: String }, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum RemoveUserField { ProfileContent, ProfileBackground, @@ -37,26 +37,26 @@ pub enum RemoveUserField { Avatar, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum RemoveChannelField { Icon, Description, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum RemoveServerField { Icon, Banner, Description, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub enum RemoveMemberField { Nickname, Avatar } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] #[serde(tag = "type")] pub enum ClientboundNotification { Error(WebSocketError), @@ -210,7 +210,7 @@ pub async fn prehandle_hook(notification: &ClientboundNotification) -> Result<() Ok(()) } -pub fn posthandle_hook(notification: &ClientboundNotification) { +pub async fn posthandle_hook(notification: &ClientboundNotification) { match ¬ification { ClientboundNotification::ChannelDelete { id } => { get_hive().hive.drop_topic(&id).ok(); @@ -218,7 +218,7 @@ pub fn posthandle_hook(notification: &ClientboundNotification) { ClientboundNotification::ChannelGroupLeave { id, user } => { get_hive() .hive - .unsubscribe(&user.to_string(), &id.to_string()) + .unsubscribe(user, id) .ok(); } ClientboundNotification::ServerDelete { id } => { @@ -227,14 +227,23 @@ pub fn posthandle_hook(notification: &ClientboundNotification) { ClientboundNotification::ServerMemberLeave { id, user } => { get_hive() .hive - .unsubscribe(&user.to_string(), &id.to_string()) + .unsubscribe(user, id) .ok(); + + if let Ok(server) = Ref::from_unchecked(id.clone()).fetch_server().await { + for channel in server.channels { + get_hive() + .hive + .unsubscribe(user, &channel) + .ok(); + } + } } ClientboundNotification::UserRelationship { id, user, status } => { if status == &RelationshipStatus::None { get_hive() .hive - .unsubscribe(&id.to_string(), &user.id.to_string()) + .unsubscribe(id, &user.id) .ok(); } } diff --git a/src/notifications/hive.rs b/src/notifications/hive.rs index 3541e53..591c6d8 100644 --- a/src/notifications/hive.rs +++ b/src/notifications/hive.rs @@ -13,8 +13,11 @@ static HIVE: OnceCell<Hive> = OnceCell::new(); pub async fn init_hive() { let hive = MongodbPubSub::new( - |ids, notification| { - super::events::posthandle_hook(¬ification); + |ids, notification: ClientboundNotification| { + let notif = notification.clone(); + async_std::task::spawn(async move { + super::events::posthandle_hook(¬if).await; + }); if let Ok(data) = to_string(¬ification) { debug!("Pushing out notification. {}", data); diff --git a/src/routes/servers/member_remove.rs b/src/routes/servers/member_remove.rs new file mode 100644 index 0000000..645dccf --- /dev/null +++ b/src/routes/servers/member_remove.rs @@ -0,0 +1,25 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; + +use mongodb::bson::doc; + +#[delete("/<target>/members/<member>")] +pub async fn req(user: User, target: Ref, member: String) -> Result<()> { + let target = target.fetch_server().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_server(&target) + .for_server() + .await?; + + if !perm.get_manage_members() { + return Err(Error::MissingPermission) + } + + let member = Ref::from(member)?.fetch_member(&target.id).await?; + if member.id.user == user.id { + return Err(Error::InvalidOperation) + } + + target.remove_member(&member.id.user).await +} diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs index 195081e..cf867c5 100644 --- a/src/routes/servers/mod.rs +++ b/src/routes/servers/mod.rs @@ -8,6 +8,7 @@ mod server_edit; mod channel_create; mod member_fetch_all; +mod member_remove; mod member_fetch; mod member_edit; @@ -21,6 +22,7 @@ pub fn routes() -> Vec<Route> { server_edit::req, channel_create::req, member_fetch_all::req, + member_remove::req, member_fetch::req, member_edit::req, invites_fetch::req -- GitLab