diff --git a/src/database/channel.rs b/src/database/channel.rs index 2b10748aaa06cb8600fd3378c74783c9d9d9ebf1..309b59886f61a194a10af3fc6fc582d68b6d766d 100644 --- a/src/database/channel.rs +++ b/src/database/channel.rs @@ -10,13 +10,18 @@ pub struct Channel { pub last_message: Option<String>, // for Direct Messages - pub recipients: Option<Vec<String>>, pub active: Option<bool>, + // for DMs / GDMs + pub recipients: Option<Vec<String>>, + + // for GDMs + pub owner: Option<String>, + // for Guilds - pub name: Option<String>, pub guild: Option<String>, // for Guilds and Group DMs + pub name: Option<String>, pub description: Option<String>, } diff --git a/src/database/guild.rs b/src/database/guild.rs index 5279061cddac02b3851a758a0cc68751e0346300..3fb73f17e855ecd446dc79d67912c8842a701fee 100644 --- a/src/database/guild.rs +++ b/src/database/guild.rs @@ -7,7 +7,7 @@ use mongodb::options::FindOneOptions; pub fn find_member_permissions<C: Into<Option<String>>>( id: String, guild: String, - channel: C, + _channel: C, ) -> u32 { let col = get_collection("guilds"); @@ -46,6 +46,7 @@ pub fn find_member_permissions<C: Into<Option<String>>>( #[derive(Serialize, Deserialize, Debug)] pub struct Member { pub id: String, + pub nickname: String, } #[derive(Serialize, Deserialize, Debug)] diff --git a/src/database/permissions.rs b/src/database/permissions.rs index 1a31abf764fc004a31f14922b74820040f062660..8e57666726d5fa27978b605cdea86dcadb7d0261 100644 --- a/src/database/permissions.rs +++ b/src/database/permissions.rs @@ -10,11 +10,11 @@ use num_enum::TryFromPrimitive; #[derive(Debug, PartialEq, Eq, TryFromPrimitive)] #[repr(u8)] pub enum Relationship { - FRIEND = 0, - OUTGOING = 1, - INCOMING = 2, - BLOCKED = 3, - BLOCKEDOTHER = 4, + Friend = 0, + Outgoing = 1, + Incoming = 2, + Blocked = 3, + BlockedOther = 4, NONE = 5, SELF = 6, } @@ -22,16 +22,16 @@ pub enum Relationship { #[derive(Debug, PartialEq, Eq, TryFromPrimitive)] #[repr(u32)] pub enum Permission { - ACCESS = 1, - CREATE_INVITE = 2, - KICK_MEMBERS = 4, - BAN_MEMBERS = 8, - READ_MESSAGES = 16, - SEND_MESSAGES = 32, - MANAGE_MESSAGES = 64, - MANAGE_CHANNELS = 128, - MANAGE_SERVER = 256, - MANAGE_ROLES = 512, + Access = 1, + CreateInvite = 2, + KickMembers = 4, + BanMembers = 8, + ReadMessages = 16, + SendMessages = 32, + ManageMessages = 64, + ManageChannels = 128, + ManageServer = 256, + ManageRoles = 512, } bitfield! { @@ -62,11 +62,11 @@ pub fn get_relationship_internal( for entry in arr { if entry.id == target_id { match entry.status { - 0 => return Relationship::FRIEND, - 1 => return Relationship::OUTGOING, - 2 => return Relationship::INCOMING, - 3 => return Relationship::BLOCKED, - 4 => return Relationship::BLOCKEDOTHER, + 0 => return Relationship::Friend, + 1 => return Relationship::Outgoing, + 2 => return Relationship::Incoming, + 3 => return Relationship::Blocked, + 4 => return Relationship::BlockedOther, _ => return Relationship::NONE, } } @@ -132,7 +132,7 @@ impl PermissionCalculator { None }; - let mut permissions = 0; + let mut permissions: u32 = 0; if let Some(guild) = guild { if let Some(_data) = guild.fetch_data_given( doc! { @@ -148,14 +148,13 @@ impl PermissionCalculator { return u32::MAX; } - permissions = guild.default_permissions; + permissions = guild.default_permissions as u32; } } if let Some(channel) = &self.channel { match channel.channel_type { 0 => { - // ? check user is part of the channel if let Some(arr) = &channel.recipients { let mut other_user = ""; for item in arr { @@ -170,8 +169,8 @@ impl PermissionCalculator { let relationship = get_relationship_internal(&self.user.id, &other_user, &relationships); - if relationship == Relationship::BLOCKED - || relationship == Relationship::BLOCKEDOTHER + if relationship == Relationship::Blocked + || relationship == Relationship::BlockedOther { permissions = 1; } else if has_mutual_connection(self.user.id, other_user.to_string()) { @@ -179,7 +178,22 @@ impl PermissionCalculator { } } } - 1 => unreachable!(), + 1 => { + if let Some(id) = &channel.owner { + if &self.user.id == id { + return u32::MAX; + } + } + + if let Some(arr) = &channel.recipients { + for item in arr { + if item == &self.user.id { + permissions = 49; + break; + } + } + } + } 2 => { // nothing implemented yet } @@ -187,7 +201,7 @@ impl PermissionCalculator { } } - permissions as u32 + permissions } pub fn as_permission(self) -> MemberPermissions<[u32; 1]> { diff --git a/src/guards/auth.rs b/src/guards/auth.rs index 0d3047024c08a3f6a218e65d82331264e642a60b..9f17f120a318dc30cccd1d7f2ccf13941977509d 100644 --- a/src/guards/auth.rs +++ b/src/guards/auth.rs @@ -16,6 +16,33 @@ pub struct UserRef { } impl UserRef { + pub fn from(id: String) -> Option<UserRef> { + match database::get_collection("users").find_one( + doc! { "_id": id }, + FindOneOptions::builder() + .projection(doc! { + "_id": 1, + "username": 1, + "email_verification.verified": 1, + }) + .build(), + ) { + Ok(result) => match result { + Some(doc) => Some(UserRef { + id: doc.get_str("_id").unwrap().to_string(), + username: doc.get_str("username").unwrap().to_string(), + email_verified: doc + .get_document("email_verification") + .unwrap() + .get_bool("verified") + .unwrap(), + }), + None => None, + }, + Err(_) => None, + } + } + pub fn fetch_data(&self, projection: Document) -> Option<Document> { database::get_collection("users") .find_one( @@ -126,30 +153,8 @@ impl<'r> FromParam<'r> for UserRef { type Error = &'r RawStr; fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> { - let col = database::get_db().collection("users"); - let result = database::get_collection("users") - .find_one( - doc! { "_id": param.to_string() }, - FindOneOptions::builder() - .projection(doc! { - "_id": 1, - "username": 1, - "email_verification.verified": 1, - }) - .build(), - ) - .unwrap(); - - if let Some(user) = result { - Ok(UserRef { - id: user.get_str("_id").unwrap().to_string(), - username: user.get_str("username").unwrap().to_string(), - email_verified: user - .get_document("email_verification") - .unwrap() - .get_bool("verified") - .unwrap(), - }) + if let Some(user) = UserRef::from(param.to_string()) { + Ok(user) } else { Err(param) } diff --git a/src/guards/channel.rs b/src/guards/channel.rs index 254ded4b3c750d45f7b58c557489b030b0aae034..11f294308889da6e65e52105c4bf50d3fcff52c9 100644 --- a/src/guards/channel.rs +++ b/src/guards/channel.rs @@ -36,6 +36,7 @@ pub struct ChannelRef { // information required for permission calculations pub recipients: Option<Vec<String>>, pub guild: Option<String>, + pub owner: Option<String>, } impl ChannelRef { diff --git a/src/routes/account.rs b/src/routes/account.rs index be323057ec47c7938d4772e6b90e19506237f8ac..46b747118f20f6c7cff9c316cc8df49470fb7b63 100644 --- a/src/routes/account.rs +++ b/src/routes/account.rs @@ -7,7 +7,7 @@ use bson::{bson, doc, from_bson, Bson::UtcDatetime}; use chrono::prelude::*; use database::user::User; use rand::{distributions::Alphanumeric, Rng}; -use rocket_contrib::json::{Json, JsonValue}; +use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; use ulid::Ulid; use validator::validate_email; @@ -167,7 +167,7 @@ pub fn resend_email(info: Json<Resend>) -> Response { doc! { "email_verification.target": info.email.clone() }, None, ) - .expect("Failed user lookup") + .expect("Failed user lookup.") { let user: User = from_bson(bson::Bson::Document(u)).expect("Failed to unwrap user."); let ev = user.email_verification; diff --git a/src/routes/channel.rs b/src/routes/channel.rs index 5c92f3f2287e037c63cc23ab06dde43783ae02d1..e74f65eb1e2a1e37028968e419e1d3d1315a6e32 100644 --- a/src/routes/channel.rs +++ b/src/routes/channel.rs @@ -1,15 +1,21 @@ use super::Response; -use crate::database::{self, message::Message, Permission, PermissionCalculator}; +use crate::database::{ + self, get_relationship_internal, message::Message, Permission, PermissionCalculator, + Relationship, +}; use crate::guards::auth::UserRef; use crate::guards::channel::ChannelRef; -use bson::{bson, doc, from_bson, Bson::UtcDatetime}; +use bson::{bson, doc, from_bson, Bson, Bson::UtcDatetime}; use chrono::prelude::*; +use hashbrown::HashSet; use num_enum::TryFromPrimitive; use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; use ulid::Ulid; +const MAXGROUPSIZE: usize = 50; + #[derive(Debug, TryFromPrimitive)] #[repr(usize)] pub enum ChannelType { @@ -32,6 +38,71 @@ macro_rules! with_permissions { }}; } +#[derive(Serialize, Deserialize)] +pub struct CreateGroup { + name: String, + nonce: String, + users: Vec<String>, +} + +/// create a new group +#[post("/create", data = "<info>")] +pub fn create_group(user: UserRef, info: Json<CreateGroup>) -> Response { + let name: String = info.name.chars().take(32).collect(); + let nonce: String = info.nonce.chars().take(32).collect(); + + let mut set = HashSet::new(); + set.insert(user.id.clone()); + for item in &info.users { + set.insert(item.clone()); + } + + if set.len() > MAXGROUPSIZE { + return Response::BadRequest(json!({ "error": "Maximum group size is 50." })); + } + + let col = database::get_collection("channels"); + if let Some(_) = col.find_one(doc! { "nonce": nonce.clone() }, None).unwrap() { + return Response::BadRequest(json!({ "error": "Group already created!" })); + } + + let relationships = user.fetch_relationships(); + + 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 + { + return Response::BadRequest(json!({ "error": "Not friends with user(s)." })); + } + + users.push(Bson::String(target.id)); + } else { + return Response::BadRequest(json!({ "error": "Specified non-existant user(s)." })); + } + } + + 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." })) + } +} + /// fetch channel information #[get("/<target>")] pub fn channel(user: UserRef, target: ChannelRef) -> Option<Response> { @@ -71,20 +142,25 @@ pub fn delete(user: UserRef, target: ChannelRef) -> Option<Response> { let permissions = with_permissions!(user, target); if !permissions.get_manage_channels() { - return Some(Response::LackingPermission(Permission::MANAGE_CHANNELS)); + return Some(Response::LackingPermission(Permission::ManageChannels)); } let col = database::get_collection("channels"); match target.channel_type { 0 => { - if col.update_one( - doc! { "_id": target.id }, - doc! { "$set": { "active": false } }, - None, - ).is_ok() { + if col + .update_one( + doc! { "_id": target.id }, + doc! { "$set": { "active": false } }, + None, + ) + .is_ok() + { Some(Response::Result(super::Status::Ok)) } else { - Some(Response::InternalServerError(json!({ "error": "Failed to close channel." }))) + Some(Response::InternalServerError( + json!({ "error": "Failed to close channel." }), + )) } } 1 => { @@ -109,7 +185,7 @@ pub fn messages(user: UserRef, target: ChannelRef) -> Option<Response> { let permissions = with_permissions!(user, target); if !permissions.get_read_messages() { - return Some(Response::LackingPermission(Permission::READ_MESSAGES)); + return Some(Response::LackingPermission(Permission::ReadMessages)); } let col = database::get_collection("messages"); @@ -146,7 +222,7 @@ pub fn send_message( let permissions = with_permissions!(user, target); if !permissions.get_send_messages() { - return Some(Response::LackingPermission(Permission::SEND_MESSAGES)); + return Some(Response::LackingPermission(Permission::SendMessages)); } let content: String = message.content.chars().take(2000).collect(); @@ -214,7 +290,7 @@ pub fn get_message(user: UserRef, target: ChannelRef, message: Message) -> Optio let permissions = with_permissions!(user, target); if !permissions.get_read_messages() { - return Some(Response::LackingPermission(Permission::READ_MESSAGES)); + return Some(Response::LackingPermission(Permission::ReadMessages)); } let prev = @@ -317,7 +393,7 @@ pub fn delete_message(user: UserRef, target: ChannelRef, message: Message) -> Op if !permissions.get_manage_messages() { if message.author != user.id { - return Some(Response::LackingPermission(Permission::MANAGE_MESSAGES)); + return Some(Response::LackingPermission(Permission::ManageMessages)); } } diff --git a/src/routes/guild.rs b/src/routes/guild.rs index 9457a3e7965e9300862a4c6425f2a79a7928a291..3f8fdb817a5692d5894db4627f51a2d796c9f7c4 100644 --- a/src/routes/guild.rs +++ b/src/routes/guild.rs @@ -3,12 +3,11 @@ use crate::database::{ self, channel::Channel, guild::{find_member_permissions, Guild}, - user::User, }; use crate::guards::auth::UserRef; use bson::{bson, doc, from_bson, Bson}; -use rocket_contrib::json::{Json, JsonValue}; +use rocket_contrib::json::Json; use serde::{Deserialize, Serialize}; use ulid::Ulid; @@ -126,7 +125,7 @@ pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response { let id = Ulid::new().to_string(); let channel_id = Ulid::new().to_string(); - if let Err(_) = channels.insert_one( + if channels.insert_one( doc! { "_id": channel_id.clone(), "type": ChannelType::GUILDCHANNEL as u32, @@ -134,7 +133,7 @@ pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response { "guild": id.clone(), }, None, - ) { + ).is_err() { return Response::InternalServerError( json!({ "error": "Failed to create guild channel." }), ); diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 96a19ec65e29261745e79c34f48aff2456bb0a69..daee20884d7a18b3f4de28e3750721209f41b2e7 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -87,6 +87,7 @@ pub fn mount(rocket: Rocket) -> Rocket { .mount( "/api/channels", routes![ + channel::create_group, channel::channel, channel::delete, channel::messages, diff --git a/src/routes/user.rs b/src/routes/user.rs index 713043f0abcf828e5c6b6a468134ea3d2b5e7fb7..560580d3ac63deb38944e47119d932d6aad2ef02 100644 --- a/src/routes/user.rs +++ b/src/routes/user.rs @@ -64,7 +64,7 @@ pub fn lookup(user: UserRef, query: Json<Query>) -> Response { if let Ok(doc) = item { let id = doc.get_str("id").unwrap(); results.push(json!({ - "id": id.clone(), + "id": id, "username": doc.get_str("username").unwrap(), "relationship": get_relationship_internal(&user.id, &id, &relationships) as u8 })); @@ -174,11 +174,11 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response { let col = database::get_collection("users"); match relationship { - Relationship::FRIEND => Response::BadRequest(json!({ "error": "Already friends." })), - Relationship::OUTGOING => { + Relationship::Friend => Response::BadRequest(json!({ "error": "Already friends." })), + Relationship::Outgoing => { Response::BadRequest(json!({ "error": "Already sent a friend request." })) } - Relationship::INCOMING => { + Relationship::Incoming => { if col .update_one( doc! { @@ -187,7 +187,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response { }, doc! { "$set": { - "relations.$.status": Relationship::FRIEND as i32 + "relations.$.status": Relationship::Friend as i32 } }, None, @@ -202,14 +202,14 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response { }, doc! { "$set": { - "relations.$.status": Relationship::FRIEND as i32 + "relations.$.status": Relationship::Friend as i32 } }, None, ) .is_ok() { - Response::Success(json!({ "status": Relationship::FRIEND as u8 })) + Response::Success(json!({ "status": Relationship::Friend as u8 })) } else { Response::InternalServerError( json!({ "error": "Failed to commit! Try re-adding them as a friend." }), @@ -221,10 +221,10 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response { ) } } - Relationship::BLOCKED => { + Relationship::Blocked => { Response::BadRequest(json!({ "error": "You have blocked this person." })) } - Relationship::BLOCKEDOTHER => { + Relationship::BlockedOther => { Response::Conflict(json!({ "error": "You have been blocked by this person." })) } Relationship::NONE => { @@ -237,7 +237,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response { "$push": { "relations": { "id": target.id.clone(), - "status": Relationship::OUTGOING as i32 + "status": Relationship::Outgoing as i32 } } }, @@ -254,7 +254,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response { "$push": { "relations": { "id": user.id, - "status": Relationship::INCOMING as i32 + "status": Relationship::Incoming as i32 } } }, @@ -262,7 +262,7 @@ pub fn add_friend(user: UserRef, target: UserRef) -> Response { ) .is_ok() { - Response::Success(json!({ "status": Relationship::OUTGOING as u8 })) + Response::Success(json!({ "status": Relationship::Outgoing as u8 })) } else { Response::InternalServerError( json!({ "error": "Failed to commit! Try re-adding them as a friend." }), @@ -287,7 +287,7 @@ pub fn remove_friend(user: UserRef, target: UserRef) -> Response { let col = database::get_collection("users"); match relationship { - Relationship::FRIEND | Relationship::OUTGOING | Relationship::INCOMING => { + Relationship::Friend | Relationship::Outgoing | Relationship::Incoming => { if col .update_one( doc! { @@ -332,8 +332,8 @@ pub fn remove_friend(user: UserRef, target: UserRef) -> Response { ) } } - Relationship::BLOCKED - | Relationship::BLOCKEDOTHER + Relationship::Blocked + | Relationship::BlockedOther | Relationship::NONE | Relationship::SELF => Response::BadRequest(json!({ "error": "This has no effect." })), } diff --git a/src/websocket/mod.rs b/src/websocket/mod.rs index 709c593f54655b58075892ee5d216b1bbe640c95..2563730477be2426805243749468fde2afedfce4 100644 --- a/src/websocket/mod.rs +++ b/src/websocket/mod.rs @@ -37,7 +37,7 @@ impl Handler for Server { if let Value::String(packet_type) = &data["type"] { match packet_type.as_str() { "authenticate" => { - if let Some(_) = self.id { + if self.id.is_some() { self.out.send( json!({ "type": "authenticate", @@ -152,7 +152,7 @@ impl Handler for Server { pub fn launch_server() { unsafe { - if let Err(_) = CLIENTS.set(RwLock::new(HashMap::new())) { + if CLIENTS.set(RwLock::new(HashMap::new())).is_err() { panic!("Failed to set CLIENTS map!"); } }