diff --git a/src/database/entities/channel.rs b/src/database/entities/channel.rs index 359fe379027f7fa13e2cd5e51d4e4ba12d6821b5..da8a61ebf16c64720db11e3190bb471c96bde790 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 ba5af0f5cdc7e4e32287706108d048e0295d81d8..778c02481a9199664ba8b73ea5e78ffd76533a6f 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 e1df309d8c4f837ada3da33abcf081897806a6a3..df696795e195a1c248fd81bbb9b08f02ca8c49f2 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 86344e47f4f94a0c9cd6bdc20fde92b77219cc73..c29427eef800caef269a2bf7adfb7f14b8196d5c 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 2b58c3be558f50892c2bc62ab7f808f63535822a..1941453e00ea8eb734153cb37a0cce11e888adc0 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 add0f82cef2b01accdf2d8e282804ad15dd8ea66..c81b958ee011b0a89373965726d736e10314b030 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 7e01b5dc61a2c785446bbe62cb46e9e64a479451..9fd0881fc17950b14b24736c7a757e9639e5f62c 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 8016cfad964a36ccf82b68a2f108dbc472fcc27d..d5e686741185c4c4a990c3071193ca3cda610d91 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 0373a5a59b9556aa2b88dd95cb5fe7c063ba0605..4ff36fd9f187413fab5f141c2a7e40d867e9e3b9 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 75875849d1a553bc80f0cd47868639050f2055ec..85bdfed986533b9e1afb3b7c9d4841db3324ace7 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 e714d4122afb02a3fb5b4da6f6426b442bed3658..dc559c203277072ff67888e2f27a4dd55ab62a6c 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 f497747c9077ca2a5735e505fd22adef09a81230..162e48dbba54d2e96f936fc0cb191508c249209b 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 cff9529c5337fd25747cebe80658d524611f5bfb..c30967f8dddcbc86d8ebf89389012dcb9df648c8 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 986b4908f1a80d96b8f415175f213e0640116647..1a7df5591c2a182e8f870f2f4bce05635ff389e6 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 f9b7a1bf416c4b3b60cda20c0ae2761ee1d11a56..f2731f88046c85819d5db761b6570bc8e1aa6978 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))) }