From 3b85dcce1406889321b7ae50699abfe0a1034250 Mon Sep 17 00:00:00 2001 From: Paul Makles <paulmakles@gmail.com> Date: Tue, 26 Jan 2021 22:05:32 +0000 Subject: [PATCH] Include group members in payload. --- src/database/entities/channel.rs | 18 ++-- src/database/entities/user.rs | 16 ++++ src/database/permissions/channel.rs | 23 +++-- src/database/permissions/mod.rs | 13 ++- src/database/permissions/user.rs | 55 +++++++----- src/notifications/payload.rs | 112 ++++++++++++++---------- src/routes/channels/delete_channel.rs | 3 +- src/routes/channels/fetch_channel.rs | 3 +- src/routes/channels/group_add_member.rs | 3 +- src/routes/channels/message_delete.rs | 3 +- src/routes/channels/message_edit.rs | 3 +- src/routes/channels/message_fetch.rs | 3 +- src/routes/channels/message_query.rs | 3 +- src/routes/channels/message_send.rs | 3 +- src/routes/users/fetch_user.rs | 25 +++--- 15 files changed, 178 insertions(+), 108 deletions(-) diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs index 359fe37..da8a61e 100644 --- a/src/database/entities/channel.rs +++ b/src/database/entities/channel.rs @@ -54,16 +54,18 @@ impl Channel { pub async fn get(id: &str) -> Result<Channel> { let doc = get_collection("channels") - .find_one( - doc! { "_id": id }, - None - ) + .find_one(doc! { "_id": id }, None) .await - .map_err(|_| Error::DatabaseError { operation: "find_one", with: "channel" })? + .map_err(|_| Error::DatabaseError { + operation: "find_one", + with: "channel", + })? .ok_or_else(|| Error::UnknownChannel)?; - - from_document::<Channel>(doc) - .map_err(|_| Error::DatabaseError { operation: "from_document", with: "channel" }) + + from_document::<Channel>(doc).map_err(|_| Error::DatabaseError { + operation: "from_document", + with: "channel", + }) } pub async fn publish(self) -> Result<()> { diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs index ba5af0f..778c024 100644 --- a/src/database/entities/user.rs +++ b/src/database/entities/user.rs @@ -1,5 +1,7 @@ use serde::{Deserialize, Serialize}; +use crate::{database::permissions::user::UserPermissions, notifications::websocket::is_online}; + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] pub enum RelationshipStatus { None, @@ -32,3 +34,17 @@ pub struct User { #[serde(skip_serializing_if = "Option::is_none")] pub online: Option<bool>, } + +impl User { + pub fn with(mut self, permissions: UserPermissions<[u32; 1]>) -> User { + if !permissions.get_view_all() { + self.relations = None; + } + + if permissions.get_view_profile() { + self.online = Some(is_online(&self.id)); + } + + self + } +} diff --git a/src/database/permissions/channel.rs b/src/database/permissions/channel.rs index e1df309..df69679 100644 --- a/src/database/permissions/channel.rs +++ b/src/database/permissions/channel.rs @@ -84,22 +84,31 @@ impl<'a> PermissionCalculator<'a> { } } Channel::DirectMessage { recipients, .. } => { - if recipients.iter().find(|x| *x == &self.perspective.id).is_some() { - if let Some(recipient) = recipients.iter().find(|x| *x != &self.perspective.id) { + if recipients + .iter() + .find(|x| *x == &self.perspective.id) + .is_some() + { + if let Some(recipient) = recipients.iter().find(|x| *x != &self.perspective.id) + { let perms = self.for_user(recipient).await?; - + if perms.get_send_message() { return Ok(ChannelPermission::View + ChannelPermission::SendMessage); } - + return Ok(ChannelPermission::View as u32); } } - + Ok(0) } Channel::Group { recipients, .. } => { - if recipients.iter().find(|x| *x == &self.perspective.id).is_some() { + if recipients + .iter() + .find(|x| *x == &self.perspective.id) + .is_some() + { Ok(ChannelPermission::View + ChannelPermission::SendMessage) } else { Ok(0) @@ -109,6 +118,6 @@ impl<'a> PermissionCalculator<'a> { } pub async fn for_channel(self) -> Result<ChannelPermissions<[u32; 1]>> { - Ok(ChannelPermissions([ self.calculate_channel().await? ])) + Ok(ChannelPermissions([self.calculate_channel().await?])) } } diff --git a/src/database/permissions/mod.rs b/src/database/permissions/mod.rs index 86344e4..c29427e 100644 --- a/src/database/permissions/mod.rs +++ b/src/database/permissions/mod.rs @@ -10,6 +10,8 @@ pub struct PermissionCalculator<'a> { user: Option<&'a User>, channel: Option<&'a Channel>, + + has_mutual_conncetion: bool, } impl<'a> PermissionCalculator<'a> { @@ -18,7 +20,9 @@ impl<'a> PermissionCalculator<'a> { perspective, user: None, - channel: None + channel: None, + + has_mutual_conncetion: false, } } @@ -35,4 +39,11 @@ impl<'a> PermissionCalculator<'a> { ..self } } + + pub fn with_mutual_connection(self) -> PermissionCalculator<'a> { + PermissionCalculator { + has_mutual_conncetion: true, + ..self + } + } } diff --git a/src/database/permissions/user.rs b/src/database/permissions/user.rs index 2b58c3b..1941453 100644 --- a/src/database/permissions/user.rs +++ b/src/database/permissions/user.rs @@ -3,9 +3,9 @@ use crate::util::result::{Error, Result}; use super::PermissionCalculator; -use std::ops; use mongodb::bson::doc; use num_enum::TryFromPrimitive; +use std::ops; #[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)] #[repr(u32)] @@ -13,7 +13,9 @@ pub enum UserPermission { Access = 1, ViewProfile = 2, SendMessage = 4, - Invite = 8 + Invite = 8, + + ViewAll = 2147483648, } bitfield! { @@ -23,9 +25,13 @@ bitfield! { pub get_view_profile, _: 30; pub get_send_message, _: 29; pub get_invite, _: 28; + + pub get_view_all, _: 0; } impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 | *b as u32 }); +impl_op_ex!(-|a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 & !(*b as u32) }); +impl_op_ex!(-|a: &u32, b: &UserPermission| -> u32 { *a & !(*b as u32) }); impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 }); pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus { @@ -44,11 +50,13 @@ pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus { impl<'a> PermissionCalculator<'a> { pub async fn calculate_user(self, target: &str) -> Result<u32> { + if &self.perspective.id == target { + return Ok(u32::MAX); + } + let mut permissions: u32 = 0; match get_relationship(&self.perspective, &target) { - RelationshipStatus::Friend => { - return Ok(u32::MAX) - } + RelationshipStatus::Friend => return Ok(u32::MAX - UserPermission::ViewAll), RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => { return Ok(UserPermission::Access as u32) } @@ -62,20 +70,25 @@ impl<'a> PermissionCalculator<'a> { _ => {} } - if get_collection("channels") - .find_one( - doc! { - "type": "Group", - "$and": { - "recipients": &self.perspective.id, - "recipients": target - } - }, - None - ) - .await - .map_err(|_| Error::DatabaseError { operation: "find", with: "channels" })? - .is_some() { + if self.has_mutual_conncetion + || get_collection("channels") + .find_one( + doc! { + "type": "Group", + "$and": { + "recipients": &self.perspective.id, + "recipients": target + } + }, + None, + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find", + with: "channels", + })? + .is_some() + { return Ok(UserPermission::Access as u32); } @@ -83,11 +96,11 @@ impl<'a> PermissionCalculator<'a> { } pub async fn for_user(self, target: &str) -> Result<UserPermissions<[u32; 1]>> { - Ok(UserPermissions([ self.calculate_user(&target).await? ])) + Ok(UserPermissions([self.calculate_user(&target).await?])) } pub async fn for_user_given(self) -> Result<UserPermissions<[u32; 1]>> { let id = &self.user.unwrap().id; - Ok(UserPermissions([ self.calculate_user(&id).await? ])) + Ok(UserPermissions([self.calculate_user(&id).await?])) } } diff --git a/src/notifications/payload.rs b/src/notifications/payload.rs index add0f82..c81b958 100644 --- a/src/notifications/payload.rs +++ b/src/notifications/payload.rs @@ -1,4 +1,6 @@ -use crate::notifications::events::ClientboundNotification; +use std::collections::HashSet; + +use crate::{database::*, notifications::events::ClientboundNotification}; use crate::{ database::{entities::User, get_collection}, util::result::{Error, Result}, @@ -9,55 +11,16 @@ use mongodb::{ options::FindOptions, }; -use super::websocket::is_online; - pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { let mut users = vec![]; + let mut user_ids: HashSet<String> = HashSet::new(); if let Some(relationships) = &user.relations { - let user_ids: Vec<String> = relationships - .iter() - .map(|relationship| relationship.id.clone()) - .collect(); - - let mut cursor = get_collection("users") - .find( - doc! { - "_id": { - "$in": user_ids - } - }, - FindOptions::builder() - .projection(doc! { "_id": 1, "username": 1 }) - .build(), - ) - .await - .map_err(|_| Error::DatabaseError { - operation: "find", - with: "users", - })?; - - while let Some(result) = cursor.next().await { - if let Ok(doc) = result { - let mut user: User = from_document(doc).map_err(|_| Error::DatabaseError { - operation: "from_document", - with: "user", - })?; - - user.relationship = Some( - relationships - .iter() - .find(|x| user.id == x.id) - .ok_or_else(|| Error::InternalError)? - .status - .clone(), - ); - - user.online = Some(is_online(&user.id)); - - users.push(user); - } - } + user_ids.extend( + relationships + .iter() + .map(|relationship| relationship.id.clone()), + ); } let mut cursor = get_collection("channels") @@ -89,10 +52,63 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { let mut channels = vec![]; while let Some(result) = cursor.next().await { if let Ok(doc) = result { - channels.push(from_document(doc).map_err(|_| Error::DatabaseError { + let channel = from_document(doc).map_err(|_| Error::DatabaseError { operation: "from_document", with: "channel", - })?); + })?; + + if let Channel::Group { recipients, .. } = &channel { + user_ids.extend(recipients.iter().cloned()); + } + + channels.push(channel); + } + } + + if user_ids.len() > 0 { + let mut cursor = get_collection("users") + .find( + doc! { + "_id": { + "$in": user_ids.into_iter().collect::<Vec<String>>() + } + }, + FindOptions::builder() + .projection(doc! { "_id": 1, "username": 1 }) + .build(), + ) + .await + .map_err(|_| Error::DatabaseError { + operation: "find", + with: "users", + })?; + + while let Some(result) = cursor.next().await { + if let Ok(doc) = result { + let mut other: User = from_document(doc).map_err(|_| Error::DatabaseError { + operation: "from_document", + with: "user", + })?; + + if let Some(relationships) = &user.relations { + other.relationship = Some( + if let Some(relationship) = relationships.iter().find(|x| other.id == x.id) + { + relationship.status.clone() + } else { + RelationshipStatus::None + }, + ); + } + + let permissions = PermissionCalculator::new(&user) + .with_mutual_connection() + .with_user(&other) + .for_user_given() + .await?; + + users.push(other.with(permissions)); + } } } diff --git a/src/routes/channels/delete_channel.rs b/src/routes/channels/delete_channel.rs index 7e01b5d..9fd0881 100644 --- a/src/routes/channels/delete_channel.rs +++ b/src/routes/channels/delete_channel.rs @@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref) -> Result<()> { let perm = permissions::PermissionCalculator::new(&user) .with_channel(&target) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_view() { Err(Error::LabelMe)? } diff --git a/src/routes/channels/fetch_channel.rs b/src/routes/channels/fetch_channel.rs index 8016cfa..d5e6867 100644 --- a/src/routes/channels/fetch_channel.rs +++ b/src/routes/channels/fetch_channel.rs @@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { let perm = permissions::PermissionCalculator::new(&user) .with_channel(&target) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_view() { Err(Error::LabelMe)? } diff --git a/src/routes/channels/group_add_member.rs b/src/routes/channels/group_add_member.rs index 0373a5a..4ff36fd 100644 --- a/src/routes/channels/group_add_member.rs +++ b/src/routes/channels/group_add_member.rs @@ -13,7 +13,8 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { let channel = target.fetch_channel().await?; let perm = permissions::PermissionCalculator::new(&user) .with_channel(&channel) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_view() { Err(Error::LabelMe)? } diff --git a/src/routes/channels/message_delete.rs b/src/routes/channels/message_delete.rs index 7587584..85bdfed 100644 --- a/src/routes/channels/message_delete.rs +++ b/src/routes/channels/message_delete.rs @@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> { let perm = permissions::PermissionCalculator::new(&user) .with_channel(&channel) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_view() { Err(Error::LabelMe)? } diff --git a/src/routes/channels/message_edit.rs b/src/routes/channels/message_edit.rs index e714d41..dc559c2 100644 --- a/src/routes/channels/message_edit.rs +++ b/src/routes/channels/message_edit.rs @@ -21,7 +21,8 @@ pub async fn req(user: User, target: Ref, msg: Ref, edit: Json<Data>) -> Result< let channel = target.fetch_channel().await?; let perm = permissions::PermissionCalculator::new(&user) .with_channel(&channel) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_view() { Err(Error::LabelMe)? } diff --git a/src/routes/channels/message_fetch.rs b/src/routes/channels/message_fetch.rs index f497747..162e48d 100644 --- a/src/routes/channels/message_fetch.rs +++ b/src/routes/channels/message_fetch.rs @@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> { let perm = permissions::PermissionCalculator::new(&user) .with_channel(&channel) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_view() { Err(Error::LabelMe)? } diff --git a/src/routes/channels/message_query.rs b/src/routes/channels/message_query.rs index cff9529..c30967f 100644 --- a/src/routes/channels/message_query.rs +++ b/src/routes/channels/message_query.rs @@ -31,7 +31,8 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json let perm = permissions::PermissionCalculator::new(&user) .with_channel(&target) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_view() { Err(Error::LabelMe)? } diff --git a/src/routes/channels/message_send.rs b/src/routes/channels/message_send.rs index 986b490..1a7df55 100644 --- a/src/routes/channels/message_send.rs +++ b/src/routes/channels/message_send.rs @@ -26,7 +26,8 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal let perm = permissions::PermissionCalculator::new(&user) .with_channel(&target) - .for_channel().await?; + .for_channel() + .await?; if !perm.get_send_message() { Err(Error::LabelMe)? } diff --git a/src/routes/users/fetch_user.rs b/src/routes/users/fetch_user.rs index f9b7a1b..f2731f8 100644 --- a/src/routes/users/fetch_user.rs +++ b/src/routes/users/fetch_user.rs @@ -1,5 +1,5 @@ +use crate::database::*; use crate::util::result::{Error, Result}; -use crate::{database::*, notifications::websocket::is_online}; use rocket_contrib::json::JsonValue; @@ -7,19 +7,16 @@ use rocket_contrib::json::JsonValue; pub async fn req(user: User, target: Ref) -> Result<JsonValue> { let mut target = target.fetch_user().await?; - if user.id != target.id { - // Check whether we are allowed to fetch this user. - let perm = permissions::PermissionCalculator::new(&user) - .with_user(&target) - .for_user_given().await?; - if !perm.get_access() { - Err(Error::LabelMe)? - } + let perm = permissions::PermissionCalculator::new(&user) + .with_user(&target) + .for_user_given() + .await?; - // Only return user relationships if the target is the caller. - target.relations = None; + if !perm.get_access() { + Err(Error::LabelMe)? + } - // Add relevant relationship + if user.id != target.id { if let Some(relationships) = &user.relations { target.relationship = relationships .iter() @@ -33,7 +30,5 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { target.relationship = Some(RelationshipStatus::User); } - target.online = Some(is_online(&target.id)); - - Ok(json!(target)) + Ok(json!(target.with(perm))) } -- GitLab