From 4e90cf1f5862e0157b80aebba0216dd9ef53cec8 Mon Sep 17 00:00:00 2001 From: Paul <paulmakles@gmail.com> Date: Sun, 6 Jun 2021 16:52:00 +0100 Subject: [PATCH] Servers: Ban and unban users + ban list. --- src/database/entities/server.rs | 31 +++++++++++++---- src/database/guards/reference.rs | 24 ++++++++++++- src/database/permissions/server.rs | 6 +++- src/routes/servers/ban_create.rs | 56 ++++++++++++++++++++++++++++++ src/routes/servers/ban_list.rs | 44 +++++++++++++++++++++++ src/routes/servers/ban_remove.rs | 35 +++++++++++++++++++ src/routes/servers/mod.rs | 7 ++++ src/util/result.rs | 2 ++ 8 files changed, 197 insertions(+), 8 deletions(-) create mode 100644 src/routes/servers/ban_create.rs create mode 100644 src/routes/servers/ban_list.rs create mode 100644 src/routes/servers/ban_remove.rs diff --git a/src/database/entities/server.rs b/src/database/entities/server.rs index 4fad955..069a4ec 100644 --- a/src/database/entities/server.rs +++ b/src/database/entities/server.rs @@ -30,7 +30,7 @@ pub struct Member { pub struct Ban { #[serde(rename = "_id")] pub id: MemberCompositeKey, - pub reason: String, + pub reason: Option<String>, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -234,6 +234,23 @@ impl Server { } pub async fn join_member(&self, id: &str) -> Result<()> { + if get_collection("server_bans") + .find_one( + doc! { + "_id.server": &self.id, + "_id.user": &id + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find_one", + with: "server_bans", + })? + .is_some() { + return Err(Error::Banned) + } + get_collection("server_members") .insert_one( doc! { @@ -260,7 +277,7 @@ impl Server { } pub async fn remove_member(&self, id: &str) -> Result<()> { - get_collection("server_members") + let result = get_collection("server_members") .delete_one( doc! { "_id": { @@ -276,11 +293,13 @@ impl Server { with: "server_members", })?; - ClientboundNotification::ServerMemberLeave { - id: self.id.clone(), - user: id.to_string() + if result.deleted_count > 0 { + ClientboundNotification::ServerMemberLeave { + id: self.id.clone(), + user: id.to_string() + } + .publish(self.id.clone()); } - .publish(self.id.clone()); Ok(()) } diff --git a/src/database/guards/reference.rs b/src/database/guards/reference.rs index 38a2be0..3fe6e06 100644 --- a/src/database/guards/reference.rs +++ b/src/database/guards/reference.rs @@ -25,7 +25,7 @@ impl Ref { Ok(r) } - pub async fn fetch<T: DeserializeOwned>(&self, collection: &'static str) -> Result<T> { + async fn fetch<T: DeserializeOwned>(&self, collection: &'static str) -> Result<T> { let doc = get_collection(&collection) .find_one( doc! { @@ -84,6 +84,28 @@ impl Ref { })?) } + pub async fn fetch_ban(&self, server: &str) -> Result<Ban> { + let doc = get_collection("server_bans") + .find_one( + doc! { + "_id.user": &self.id, + "_id.server": server + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find_one", + with: "server_ban", + })? + .ok_or_else(|| Error::NotFound)?; + + Ok(from_document::<Ban>(doc).map_err(|_| Error::DatabaseError { + operation: "from_document", + with: "server_ban", + })?) + } + pub async fn fetch_message(&self, channel: &Channel) -> Result<Message> { let message: Message = self.fetch("messages").await?; if &message.channel != channel.id() { diff --git a/src/database/permissions/server.rs b/src/database/permissions/server.rs index 276e248..c4af2dd 100644 --- a/src/database/permissions/server.rs +++ b/src/database/permissions/server.rs @@ -12,7 +12,9 @@ pub enum ServerPermission { ManageMembers = 0b00000000000000000000000000000010, // 2 ManageChannels = 0b00000000000000000000000000000100, // 4 ManageServer = 0b00000000000000000000000000001000, // 8 - // 8 bits of space + KickMembers = 0b00000000000000000000000000010000, // 16 + BanMembers = 0b00000000000000000000000000100000, // 32 + // 6 bits of space ChangeNickname = 0b00000000000000000001000000000000, // 4096 ManageNicknames = 0b00000000000000000010000000000000, // 8192 ChangeAvatar = 0b00000000000000000100000000000000, // 16392 @@ -30,6 +32,8 @@ bitfield! { pub get_manage_members, _: 30; pub get_manage_channels, _: 29; pub get_manage_server, _: 28; + pub get_kick_members, _: 27; + pub get_ban_members, _: 26; pub get_change_nickname, _: 19; pub get_manage_nicknames, _: 18; diff --git a/src/routes/servers/ban_create.rs b/src/routes/servers/ban_create.rs new file mode 100644 index 0000000..76a7428 --- /dev/null +++ b/src/routes/servers/ban_create.rs @@ -0,0 +1,56 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; + +use mongodb::bson::doc; +use validator::Validate; +use rocket_contrib::json::Json; +use serde::{Serialize, Deserialize}; + +#[derive(Validate, Serialize, Deserialize)] +pub struct Data { + #[validate(length(min = 1, max = 1024))] + reason: Option<String>, +} + +#[put("/<server>/bans/<target>", data = "<data>")] +pub async fn req(user: User, server: Ref, target: Ref, data: Json<Data>) -> Result<()> { + let data = data.into_inner(); + data.validate() + .map_err(|error| Error::FailedValidation { error })?; + + let server = server.fetch_server().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_server(&server) + .for_server() + .await?; + + if !perm.get_ban_members() { + Err(Error::MissingPermission)? + } + + let target = target.fetch_user().await?; + let mut document = doc! { + "_id": { + "server": &server.id, + "user": &target.id + } + }; + + if let Some(reason) = data.reason { + document.insert("reason", reason); + } + + get_collection("server_bans") + .insert_one( + document, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "insert_one", + with: "server_ban" + })?; + + server.remove_member(&target.id).await +} diff --git a/src/routes/servers/ban_list.rs b/src/routes/servers/ban_list.rs new file mode 100644 index 0000000..56899aa --- /dev/null +++ b/src/routes/servers/ban_list.rs @@ -0,0 +1,44 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; + +use futures::StreamExt; +use rocket_contrib::json::JsonValue; +use mongodb::bson::{doc, from_document}; + +#[get("/<target>/bans")] +pub async fn req(user: User, target: Ref) -> Result<JsonValue> { + let target = target.fetch_server().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_server(&target) + .for_server() + .await?; + + if !perm.get_manage_members() { + Err(Error::MissingPermission)? + } + + let mut cursor = get_collection("server_bans") + .find( + doc! { + "_id.server": target.id + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find", + with: "server_bans", + })?; + + let mut bans = vec![]; + while let Some(result) = cursor.next().await { + if let Ok(doc) = result { + if let Ok(ban) = from_document::<Ban>(doc) { + bans.push(ban); + } + } + } + + Ok(json!(bans)) +} diff --git a/src/routes/servers/ban_remove.rs b/src/routes/servers/ban_remove.rs new file mode 100644 index 0000000..ef27a50 --- /dev/null +++ b/src/routes/servers/ban_remove.rs @@ -0,0 +1,35 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; + +use mongodb::bson::doc; + +#[delete("/<server>/bans/<target>")] +pub async fn req(user: User, server: Ref, target: Ref) -> Result<()> { + let server = server.fetch_server().await?; + + let perm = permissions::PermissionCalculator::new(&user) + .with_server(&server) + .for_server() + .await?; + + if !perm.get_ban_members() { + Err(Error::MissingPermission)? + } + + let target = target.fetch_ban(&server.id).await?; + get_collection("server_bans") + .delete_one( + doc! { + "_id.server": &server.id, + "_id.user": &target.id.user + }, + None + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "delete_one", + with: "server_ban" + })?; + + Ok(()) +} diff --git a/src/routes/servers/mod.rs b/src/routes/servers/mod.rs index cf867c5..26638b4 100644 --- a/src/routes/servers/mod.rs +++ b/src/routes/servers/mod.rs @@ -12,6 +12,10 @@ mod member_remove; mod member_fetch; mod member_edit; +mod ban_create; +mod ban_remove; +mod ban_list; + mod invites_fetch; pub fn routes() -> Vec<Route> { @@ -25,6 +29,9 @@ pub fn routes() -> Vec<Route> { member_remove::req, member_fetch::req, member_edit::req, + ban_create::req, + ban_remove::req, + ban_list::req, invites_fetch::req ] } diff --git a/src/util/result.rs b/src/util/result.rs index 44d4bad..b3225a0 100644 --- a/src/util/result.rs +++ b/src/util/result.rs @@ -38,6 +38,7 @@ pub enum Error { // ? Server related errors. UnknownServer, + Banned, // ? General errors. TooManyIds, @@ -87,6 +88,7 @@ impl<'r> Responder<'r, 'static> for Error { Error::NotInGroup => Status::NotFound, Error::UnknownServer => Status::NotFound, + Error::Banned => Status::Forbidden, Error::FailedValidation { .. } => Status::UnprocessableEntity, Error::DatabaseError { .. } => Status::InternalServerError, -- GitLab