Include group members in payload.

This commit is contained in:
Paul Makles
2021-01-26 22:05:32 +00:00
parent f42480886b
commit 3b85dcce14
15 changed files with 179 additions and 109 deletions

View File

@@ -54,16 +54,18 @@ impl Channel {
pub async fn get(id: &str) -> Result<Channel> { pub async fn get(id: &str) -> Result<Channel> {
let doc = get_collection("channels") let doc = get_collection("channels")
.find_one( .find_one(doc! { "_id": id }, None)
doc! { "_id": id },
None
)
.await .await
.map_err(|_| Error::DatabaseError { operation: "find_one", with: "channel" })? .map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "channel",
})?
.ok_or_else(|| Error::UnknownChannel)?; .ok_or_else(|| Error::UnknownChannel)?;
from_document::<Channel>(doc) from_document::<Channel>(doc).map_err(|_| Error::DatabaseError {
.map_err(|_| Error::DatabaseError { operation: "from_document", with: "channel" }) operation: "from_document",
with: "channel",
})
} }
pub async fn publish(self) -> Result<()> { pub async fn publish(self) -> Result<()> {

View File

@@ -1,5 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{database::permissions::user::UserPermissions, notifications::websocket::is_online};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub enum RelationshipStatus { pub enum RelationshipStatus {
None, None,
@@ -32,3 +34,17 @@ pub struct User {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub online: Option<bool>, 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
}
}

View File

@@ -84,22 +84,31 @@ impl<'a> PermissionCalculator<'a> {
} }
} }
Channel::DirectMessage { recipients, .. } => { Channel::DirectMessage { recipients, .. } => {
if recipients.iter().find(|x| *x == &self.perspective.id).is_some() { if recipients
if let Some(recipient) = recipients.iter().find(|x| *x != &self.perspective.id) { .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?; let perms = self.for_user(recipient).await?;
if perms.get_send_message() { if perms.get_send_message() {
return Ok(ChannelPermission::View + ChannelPermission::SendMessage); return Ok(ChannelPermission::View + ChannelPermission::SendMessage);
} }
return Ok(ChannelPermission::View as u32); return Ok(ChannelPermission::View as u32);
} }
} }
Ok(0) Ok(0)
} }
Channel::Group { recipients, .. } => { 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) Ok(ChannelPermission::View + ChannelPermission::SendMessage)
} else { } else {
Ok(0) Ok(0)
@@ -109,6 +118,6 @@ impl<'a> PermissionCalculator<'a> {
} }
pub async fn for_channel(self) -> Result<ChannelPermissions<[u32; 1]>> { pub async fn for_channel(self) -> Result<ChannelPermissions<[u32; 1]>> {
Ok(ChannelPermissions([ self.calculate_channel().await? ])) Ok(ChannelPermissions([self.calculate_channel().await?]))
} }
} }

View File

@@ -10,6 +10,8 @@ pub struct PermissionCalculator<'a> {
user: Option<&'a User>, user: Option<&'a User>,
channel: Option<&'a Channel>, channel: Option<&'a Channel>,
has_mutual_conncetion: bool,
} }
impl<'a> PermissionCalculator<'a> { impl<'a> PermissionCalculator<'a> {
@@ -18,7 +20,9 @@ impl<'a> PermissionCalculator<'a> {
perspective, perspective,
user: None, user: None,
channel: None channel: None,
has_mutual_conncetion: false,
} }
} }
@@ -35,4 +39,11 @@ impl<'a> PermissionCalculator<'a> {
..self ..self
} }
} }
pub fn with_mutual_connection(self) -> PermissionCalculator<'a> {
PermissionCalculator {
has_mutual_conncetion: true,
..self
}
}
} }

View File

@@ -3,9 +3,9 @@ use crate::util::result::{Error, Result};
use super::PermissionCalculator; use super::PermissionCalculator;
use std::ops;
use mongodb::bson::doc; use mongodb::bson::doc;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use std::ops;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
#[repr(u32)] #[repr(u32)]
@@ -13,7 +13,9 @@ pub enum UserPermission {
Access = 1, Access = 1,
ViewProfile = 2, ViewProfile = 2,
SendMessage = 4, SendMessage = 4,
Invite = 8 Invite = 8,
ViewAll = 2147483648,
} }
bitfield! { bitfield! {
@@ -23,9 +25,13 @@ bitfield! {
pub get_view_profile, _: 30; pub get_view_profile, _: 30;
pub get_send_message, _: 29; pub get_send_message, _: 29;
pub get_invite, _: 28; 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: &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 }); impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 });
pub fn get_relationship(a: &User, b: &str) -> RelationshipStatus { 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> { impl<'a> PermissionCalculator<'a> {
pub async fn calculate_user(self, target: &str) -> Result<u32> { pub async fn calculate_user(self, target: &str) -> Result<u32> {
if &self.perspective.id == target {
return Ok(u32::MAX);
}
let mut permissions: u32 = 0; let mut permissions: u32 = 0;
match get_relationship(&self.perspective, &target) { match get_relationship(&self.perspective, &target) {
RelationshipStatus::Friend => { RelationshipStatus::Friend => return Ok(u32::MAX - UserPermission::ViewAll),
return Ok(u32::MAX)
}
RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => { RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
return Ok(UserPermission::Access as u32) return Ok(UserPermission::Access as u32)
} }
@@ -62,20 +70,25 @@ impl<'a> PermissionCalculator<'a> {
_ => {} _ => {}
} }
if get_collection("channels") if self.has_mutual_conncetion
.find_one( || get_collection("channels")
doc! { .find_one(
"type": "Group", doc! {
"$and": { "type": "Group",
"recipients": &self.perspective.id, "$and": {
"recipients": target "recipients": &self.perspective.id,
} "recipients": target
}, }
None },
) None,
.await )
.map_err(|_| Error::DatabaseError { operation: "find", with: "channels" })? .await
.is_some() { .map_err(|_| Error::DatabaseError {
operation: "find",
with: "channels",
})?
.is_some()
{
return Ok(UserPermission::Access as u32); 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]>> { 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]>> { pub async fn for_user_given(self) -> Result<UserPermissions<[u32; 1]>> {
let id = &self.user.unwrap().id; let id = &self.user.unwrap().id;
Ok(UserPermissions([ self.calculate_user(&id).await? ])) Ok(UserPermissions([self.calculate_user(&id).await?]))
} }
} }

View File

@@ -1,4 +1,6 @@
use crate::notifications::events::ClientboundNotification; use std::collections::HashSet;
use crate::{database::*, notifications::events::ClientboundNotification};
use crate::{ use crate::{
database::{entities::User, get_collection}, database::{entities::User, get_collection},
util::result::{Error, Result}, util::result::{Error, Result},
@@ -9,55 +11,16 @@ use mongodb::{
options::FindOptions, options::FindOptions,
}; };
use super::websocket::is_online;
pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
let mut users = vec![]; let mut users = vec![];
let mut user_ids: HashSet<String> = HashSet::new();
if let Some(relationships) = &user.relations { if let Some(relationships) = &user.relations {
let user_ids: Vec<String> = relationships user_ids.extend(
.iter() relationships
.map(|relationship| relationship.id.clone()) .iter()
.collect(); .map(|relationship| relationship.id.clone()),
);
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);
}
}
} }
let mut cursor = get_collection("channels") let mut cursor = get_collection("channels")
@@ -89,10 +52,63 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
let mut channels = vec![]; let mut channels = vec![];
while let Some(result) = cursor.next().await { while let Some(result) = cursor.next().await {
if let Ok(doc) = result { 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", operation: "from_document",
with: "channel", 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));
}
} }
} }

View File

@@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .with_channel(&target)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .with_channel(&target)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -13,7 +13,8 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
let channel = target.fetch_channel().await?; let channel = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -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 channel = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -9,7 +9,8 @@ pub async fn req(user: User, target: Ref, msg: Ref) -> Result<JsonValue> {
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -31,7 +31,8 @@ pub async fn req(user: User, target: Ref, options: Form<Options>) -> Result<Json
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .with_channel(&target)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -26,7 +26,8 @@ pub async fn req(user: User, target: Ref, message: Json<Data>) -> Result<JsonVal
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target) .with_channel(&target)
.for_channel().await?; .for_channel()
.await?;
if !perm.get_send_message() { if !perm.get_send_message() {
Err(Error::LabelMe)? Err(Error::LabelMe)?
} }

View File

@@ -1,5 +1,5 @@
use crate::database::*;
use crate::util::result::{Error, Result}; use crate::util::result::{Error, Result};
use crate::{database::*, notifications::websocket::is_online};
use rocket_contrib::json::JsonValue; use rocket_contrib::json::JsonValue;
@@ -7,19 +7,16 @@ use rocket_contrib::json::JsonValue;
pub async fn req(user: User, target: Ref) -> Result<JsonValue> { pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let mut target = target.fetch_user().await?; let mut target = target.fetch_user().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_user(&target)
.for_user_given()
.await?;
if !perm.get_access() {
Err(Error::LabelMe)?
}
if user.id != target.id { 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)?
}
// Only return user relationships if the target is the caller.
target.relations = None;
// Add relevant relationship
if let Some(relationships) = &user.relations { if let Some(relationships) = &user.relations {
target.relationship = relationships target.relationship = relationships
.iter() .iter()
@@ -33,7 +30,5 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
target.relationship = Some(RelationshipStatus::User); target.relationship = Some(RelationshipStatus::User);
} }
target.online = Some(is_online(&target.id)); Ok(json!(target.with(perm)))
Ok(json!(target))
} }