From a910bd657ae13886754d19eec014d76e2276a966 Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Fri, 10 Apr 2020 13:09:04 +0100 Subject: [PATCH] Add / remove from group + clean up code. --- src/database/message.rs | 4 + src/guards/auth.rs | 17 ---- src/guards/channel.rs | 18 ---- src/guards/guild.rs | 18 ---- src/routes/channel.rs | 195 ++++++++++++++++++++++++++++++++-------- src/routes/guild.rs | 6 +- src/routes/mod.rs | 2 + src/util/mod.rs | 2 +- 8 files changed, 170 insertions(+), 92 deletions(-) diff --git a/src/database/message.rs b/src/database/message.rs index 0e18d23..dd273a7 100644 --- a/src/database/message.rs +++ b/src/database/message.rs @@ -20,3 +20,7 @@ pub struct Message { pub previous_content: Option<Vec<PreviousEntry>>, } + +// ? TODO: write global send message +// ? pub fn send_message(); +// ? handle websockets? diff --git a/src/guards/auth.rs b/src/guards/auth.rs index c9b2974..3c503df 100644 --- a/src/guards/auth.rs +++ b/src/guards/auth.rs @@ -160,20 +160,3 @@ impl<'r> FromParam<'r> for UserRef { } } } - -impl<'r> FromParam<'r> for User { - type Error = &'r RawStr; - - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - let col = database::get_collection("users"); - let result = col - .find_one(doc! { "_id": param.to_string() }, None) - .unwrap(); - - if let Some(user) = result { - Ok(from_bson(bson::Bson::Document(user)).expect("Failed to unwrap user.")) - } else { - Err(param) - } - } -} diff --git a/src/guards/channel.rs b/src/guards/channel.rs index 10f4116..7b64e2b 100644 --- a/src/guards/channel.rs +++ b/src/guards/channel.rs @@ -6,26 +6,8 @@ use serde::{Deserialize, Serialize}; use crate::database; -use database::channel::Channel; use database::message::Message; -impl<'r> FromParam<'r> for Channel { - type Error = &'r RawStr; - - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - let col = database::get_collection("channels"); - let result = col - .find_one(doc! { "_id": param.to_string() }, None) - .unwrap(); - - if let Some(channel) = result { - Ok(from_bson(bson::Bson::Document(channel)).expect("Failed to unwrap channel.")) - } else { - Err(param) - } - } -} - #[derive(Serialize, Deserialize, Debug, Clone)] pub struct ChannelRef { #[serde(rename = "_id")] diff --git a/src/guards/guild.rs b/src/guards/guild.rs index aa57c55..1ca1f09 100644 --- a/src/guards/guild.rs +++ b/src/guards/guild.rs @@ -5,7 +5,6 @@ use rocket::request::FromParam; use serde::{Deserialize, Serialize}; use crate::database; -use crate::database::guild::Guild; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct GuildRef { @@ -74,20 +73,3 @@ impl<'r> FromParam<'r> for GuildRef { } } } - -impl<'r> FromParam<'r> for Guild { - type Error = &'r RawStr; - - fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - let col = database::get_collection("guilds"); - let result = col - .find_one(doc! { "_id": param.to_string() }, None) - .unwrap(); - - if let Some(guild) = result { - Ok(from_bson(bson::Bson::Document(guild)).expect("Failed to unwrap guild.")) - } else { - Err(param) - } - } -} diff --git a/src/routes/channel.rs b/src/routes/channel.rs index 0b6b3d1..e2a5e52 100644 --- a/src/routes/channel.rs +++ b/src/routes/channel.rs @@ -1,7 +1,7 @@ use super::Response; use crate::database::{ - self, get_relationship_internal, message::Message, Permission, PermissionCalculator, - Relationship, + self, get_relationship, get_relationship_internal, message::Message, Permission, + PermissionCalculator, Relationship, }; use crate::guards::auth::UserRef; use crate::guards::channel::ChannelRef; @@ -9,7 +9,7 @@ use crate::util::vec_to_set; use bson::{doc, from_bson, Bson, Bson::UtcDatetime}; use chrono::prelude::*; -use hashbrown::HashSet; +use mongodb::options::FindOptions; use num_enum::TryFromPrimitive; use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; @@ -64,40 +64,61 @@ pub fn create_group(user: UserRef, info: Json<CreateGroup>) -> Response { return Response::BadRequest(json!({ "error": "Group already created!" })); } - let relationships = user.fetch_relationships(); + let mut query = vec![]; + for item in &set { + if item == &user.id { + continue; + } - let mut users = vec![]; - for item in set { - if let Some(target) = UserRef::from(item) { - if get_relationship_internal(&user.id, &target.id, &relationships) - != Relationship::Friend - { + query.push(Bson::String(item.clone())); + } + + if let Ok(result) = database::get_collection("users").find( + doc! { + "_id": { + "$in": &query + } + }, + FindOptions::builder().limit(query.len() as i64).build(), + ) { + if result.count() != query.len() { + return Response::BadRequest(json!({ "error": "Specified non-existant user(s)." })); + } + + let relationships = user.fetch_relationships(); + for item in set { + if item == user.id { + continue; + } + + if get_relationship_internal(&user.id, &item, &relationships) != Relationship::Friend { return Response::BadRequest(json!({ "error": "Not friends with user(s)." })); } + } + + query.push(Bson::String(user.id.clone())); - users.push(Bson::String(target.id)); + let id = Ulid::new().to_string(); + if col + .insert_one( + doc! { + "_id": id.clone(), + "nonce": nonce, + "type": ChannelType::GROUPDM as u32, + "recipients": &query, + "name": name, + "owner": &user.id, + }, + None, + ) + .is_ok() + { + Response::Success(json!({ "id": id })) } else { - return Response::BadRequest(json!({ "error": "Specified non-existant user(s)." })); + Response::InternalServerError(json!({ "error": "Failed to create guild channel." })) } - } - - let id = Ulid::new().to_string(); - if col - .insert_one( - doc! { - "_id": id.clone(), - "nonce": nonce, - "type": ChannelType::GROUPDM as u32, - "recipients": users, - "name": name, - }, - None, - ) - .is_ok() - { - Response::Success(json!({ "id": id })) } else { - Response::InternalServerError(json!({ "error": "Failed to create guild channel." })) + Response::InternalServerError(json!({ "error": "Failed to validate users." })) } } @@ -150,6 +171,102 @@ pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> { } } +/// [groups] add user to channel +#[put("/<target>/recipients/<member>")] +pub fn add_member(user: UserRef, target: ChannelRef, member: UserRef) -> Option<Response> { + if target.channel_type != 1 { + return Some(Response::BadRequest(json!({ "error": "Not a group DM." }))); + } + + with_permissions!(user, target); + + let recp = target.recipients.unwrap(); + if recp.len() == 50 { + return Some(Response::BadRequest( + json!({ "error": "Maximum group size is 50." }), + )); + } + + let set = vec_to_set(&recp); + if set.get(&member.id).is_some() { + return Some(Response::BadRequest( + json!({ "error": "User already in group!" }), + )); + } + + match get_relationship(&user, &member) { + Relationship::Friend => { + if database::get_collection("channels") + .update_one( + doc! { "_id": &target.id }, + doc! { + "$push": { + "recipients": &member.id + } + }, + None, + ) + .is_ok() + { + Some(Response::Result(super::Status::Ok)) + } else { + Some(Response::InternalServerError( + json!({ "error": "Failed to add user to group." }), + )) + } + } + _ => Some(Response::BadRequest( + json!({ "error": "Not friends with user." }), + )), + } +} + +/// [groups] remove user from channel +#[delete("/<target>/recipients/<member>")] +pub fn remove_member(user: UserRef, target: ChannelRef, member: UserRef) -> Option<Response> { + if target.channel_type != 1 { + return Some(Response::BadRequest(json!({ "error": "Not a group DM." }))); + } + + if &user.id == &member.id { + return Some(Response::BadRequest( + json!({ "error": "Cannot kick yourself, leave the channel instead." }), + )); + } + + let permissions = with_permissions!(user, target); + + if !permissions.get_kick_members() { + return Some(Response::LackingPermission(Permission::KickMembers)); + } + + let set = vec_to_set(&target.recipients.unwrap()); + if set.get(&member.id).is_none() { + return Some(Response::BadRequest( + json!({ "error": "User not in group!" }), + )); + } + + if database::get_collection("channels") + .update_one( + doc! { "_id": &target.id }, + doc! { + "$pull": { + "recipients": &member.id + } + }, + None, + ) + .is_ok() + { + Some(Response::Result(super::Status::Ok)) + } else { + Some(Response::InternalServerError( + json!({ "error": "Failed to add user to group." }), + )) + } +} + /// delete channel /// or leave group DM /// or close DM conversation @@ -297,7 +414,11 @@ pub fn send_message( let nonce: String = message.nonce.chars().take(32).collect(); let col = database::get_collection("messages"); - if let Some(_) = col.find_one(doc! { "nonce": nonce.clone() }, None).unwrap() { + if col + .find_one(doc! { "nonce": nonce.clone() }, None) + .unwrap() + .is_some() + { return Some(Response::BadRequest( json!({ "error": "Message already sent!" }), )); @@ -309,10 +430,10 @@ pub fn send_message( .insert_one( doc! { "_id": id.clone(), - "nonce": nonce.clone(), + "nonce": nonce, "channel": target.id.clone(), - "author": user.id.clone(), - "content": content.clone(), + "author": user.id, + "content": content, }, None, ) @@ -321,7 +442,7 @@ pub fn send_message( if target.channel_type == ChannelType::DM as u8 { let col = database::get_collection("channels"); col.update_one( - doc! { "_id": target.id.clone() }, + doc! { "_id": &target.id }, doc! { "$set": { "active": true } }, None, ) @@ -420,7 +541,7 @@ pub fn edit_message( doc! { "$set": { "content": edit.content.clone(), - "edited": UtcDatetime(edited.clone()) + "edited": UtcDatetime(edited) }, "$push": { "previous_content": { @@ -467,7 +588,7 @@ pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Op let col = database::get_collection("messages"); - match col.delete_one(doc! { "_id": message.id.clone() }, None) { + match col.delete_one(doc! { "_id": &message.id }, None) { Ok(_) => { /*websocket::queue_message( get_recipients(&target), diff --git a/src/routes/guild.rs b/src/routes/guild.rs index daadfee..42a8d9f 100644 --- a/src/routes/guild.rs +++ b/src/routes/guild.rs @@ -127,7 +127,11 @@ pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response { let channels = database::get_collection("channels"); let col = database::get_collection("guilds"); - if let Some(_) = col.find_one(doc! { "nonce": nonce.clone() }, None).unwrap() { + if col + .find_one(doc! { "nonce": nonce.clone() }, None) + .unwrap() + .is_some() + { return Response::BadRequest(json!({ "error": "Guild already created!" })); } diff --git a/src/routes/mod.rs b/src/routes/mod.rs index daee208..5ac65b0 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -89,6 +89,8 @@ pub fn mount(rocket: Rocket) -> Rocket { routes![ channel::create_group, channel::channel, + channel::add_member, + channel::remove_member, channel::delete, channel::messages, channel::get_message, diff --git a/src/util/mod.rs b/src/util/mod.rs index fe40409..c2f6c42 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,6 +1,6 @@ use hashbrown::HashSet; use std::iter::FromIterator; -pub fn vec_to_set<T: Clone + Eq + std::hash::Hash>(data: &Vec<T>) -> HashSet<T> { +pub fn vec_to_set<T: Clone + Eq + std::hash::Hash>(data: &[T]) -> HashSet<T> { HashSet::from_iter(data.iter().cloned()) } -- GitLab