Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Showing
with 497 additions and 168 deletions
use crate::database::*; use crate::database::*;
use crate::util::result::Result; use crate::util::result::{Error, Result};
use super::PermissionCalculator; use super::PermissionCalculator;
...@@ -9,11 +9,33 @@ use std::ops; ...@@ -9,11 +9,33 @@ use std::ops;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
#[repr(u32)] #[repr(u32)]
pub enum ChannelPermission { pub enum ChannelPermission {
View = 1, View = 0b00000000000000000000000000000001, // 1
SendMessage = 2, SendMessage = 0b00000000000000000000000000000010, // 2
ManageMessages = 4, ManageMessages = 0b00000000000000000000000000000100, // 4
ManageChannel = 8, ManageChannel = 0b00000000000000000000000000001000, // 8
VoiceCall = 16, VoiceCall = 0b00000000000000000000000000010000, // 16
InviteOthers = 0b00000000000000000000000000100000, // 32
EmbedLinks = 0b00000000000000000000000001000000, // 64
UploadFiles = 0b00000000000000000000000010000000, // 128
}
lazy_static! {
pub static ref DEFAULT_PERMISSION_DM: u32 =
ChannelPermission::View
+ ChannelPermission::SendMessage
+ ChannelPermission::ManageChannel
+ ChannelPermission::VoiceCall
+ ChannelPermission::InviteOthers
+ ChannelPermission::EmbedLinks
+ ChannelPermission::UploadFiles;
pub static ref DEFAULT_PERMISSION_SERVER: u32 =
ChannelPermission::View
+ ChannelPermission::SendMessage
+ ChannelPermission::VoiceCall
+ ChannelPermission::InviteOthers
+ ChannelPermission::EmbedLinks
+ ChannelPermission::UploadFiles;
} }
impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 }); impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 });
...@@ -27,6 +49,9 @@ bitfield! { ...@@ -27,6 +49,9 @@ bitfield! {
pub get_manage_messages, _: 29; pub get_manage_messages, _: 29;
pub get_manage_channel, _: 28; pub get_manage_channel, _: 28;
pub get_voice_call, _: 27; pub get_voice_call, _: 27;
pub get_invite_others, _: 26;
pub get_embed_links, _: 25;
pub get_upload_files, _: 24;
} }
impl<'a> PermissionCalculator<'a> { impl<'a> PermissionCalculator<'a> {
...@@ -40,7 +65,7 @@ impl<'a> PermissionCalculator<'a> { ...@@ -40,7 +65,7 @@ impl<'a> PermissionCalculator<'a> {
match channel { match channel {
Channel::SavedMessages { user: owner, .. } => { Channel::SavedMessages { user: owner, .. } => {
if &self.perspective.id == owner { if &self.perspective.id == owner {
Ok(u32::MAX - ChannelPermission::VoiceCall as u32) Ok(u32::MAX)
} else { } else {
Ok(0) Ok(0)
} }
...@@ -56,9 +81,7 @@ impl<'a> PermissionCalculator<'a> { ...@@ -56,9 +81,7 @@ impl<'a> PermissionCalculator<'a> {
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 return Ok(*DEFAULT_PERMISSION_DM);
+ ChannelPermission::SendMessage
+ ChannelPermission::VoiceCall);
} }
return Ok(ChannelPermission::View as u32); return Ok(ChannelPermission::View as u32);
...@@ -67,20 +90,63 @@ impl<'a> PermissionCalculator<'a> { ...@@ -67,20 +90,63 @@ impl<'a> PermissionCalculator<'a> {
Ok(0) Ok(0)
} }
Channel::Group { recipients, .. } => { Channel::Group { recipients, permissions, owner, .. } => {
if &self.perspective.id == owner {
return Ok(*DEFAULT_PERMISSION_DM)
}
if recipients if recipients
.iter() .iter()
.find(|x| *x == &self.perspective.id) .find(|x| *x == &self.perspective.id)
.is_some() .is_some()
{ {
Ok(ChannelPermission::View if let Some(permissions) = permissions {
+ ChannelPermission::SendMessage Ok(permissions.clone() as u32)
+ ChannelPermission::ManageChannel } else {
+ ChannelPermission::VoiceCall) Ok(*DEFAULT_PERMISSION_DM)
}
} else { } else {
Ok(0) Ok(0)
} }
} }
Channel::TextChannel { server, default_permissions, role_permissions, .. }
| Channel::VoiceChannel { server, default_permissions, role_permissions, .. } => {
let server = Ref::from_unchecked(server.clone()).fetch_server().await?;
if self.perspective.id == server.owner {
Ok(u32::MAX)
} else {
match Ref::from_unchecked(self.perspective.id.clone()).fetch_member(&server.id).await {
Ok(member) => {
let mut perm = if let Some(permission) = default_permissions {
*permission as u32
} else {
server.default_permissions.1 as u32
};
if let Some(roles) = member.roles {
for role in roles {
if let Some(permission) = role_permissions.get(&role) {
perm |= *permission as u32;
}
if let Some(server_role) = server.roles.get(&role) {
perm |= server_role.permissions.1 as u32;
}
}
}
Ok(perm)
}
Err(error) => {
match &error {
Error::NotFound => Ok(0),
_ => Err(error)
}
}
}
}
}
} }
} }
......
pub use crate::database::*; pub use crate::database::*;
pub mod channel; pub mod channel;
pub mod server;
pub mod user; pub mod user;
pub use user::get_relationship; pub use user::get_relationship;
...@@ -12,6 +13,7 @@ pub struct PermissionCalculator<'a> { ...@@ -12,6 +13,7 @@ pub struct PermissionCalculator<'a> {
relationship: Option<&'a RelationshipStatus>, relationship: Option<&'a RelationshipStatus>,
channel: Option<&'a Channel>, channel: Option<&'a Channel>,
server: Option<&'a Server>, server: Option<&'a Server>,
// member: Option<&'a Member>,
has_mutual_connection: bool, has_mutual_connection: bool,
} }
...@@ -25,6 +27,7 @@ impl<'a> PermissionCalculator<'a> { ...@@ -25,6 +27,7 @@ impl<'a> PermissionCalculator<'a> {
relationship: None, relationship: None,
channel: None, channel: None,
server: None, server: None,
// member: None,
has_mutual_connection: false, has_mutual_connection: false,
} }
...@@ -58,6 +61,13 @@ impl<'a> PermissionCalculator<'a> { ...@@ -58,6 +61,13 @@ impl<'a> PermissionCalculator<'a> {
} }
} }
/* pub fn with_member(self, member: &'a Member) -> PermissionCalculator {
PermissionCalculator {
member: Some(&member),
..self
}
} */
pub fn with_mutual_connection(self) -> PermissionCalculator<'a> { pub fn with_mutual_connection(self) -> PermissionCalculator<'a> {
PermissionCalculator { PermissionCalculator {
has_mutual_connection: true, has_mutual_connection: true,
......
use crate::database::*; use crate::util::result::{Error, Result};
use crate::util::result::Result;
use super::PermissionCalculator; use super::PermissionCalculator;
use super::Ref;
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use std::ops; use std::ops;
...@@ -9,7 +9,25 @@ use std::ops; ...@@ -9,7 +9,25 @@ use std::ops;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
#[repr(u32)] #[repr(u32)]
pub enum ServerPermission { pub enum ServerPermission {
View = 1, View = 0b00000000000000000000000000000001, // 1
ManageRoles = 0b00000000000000000000000000000010, // 2
ManageChannels = 0b00000000000000000000000000000100, // 4
ManageServer = 0b00000000000000000000000000001000, // 8
KickMembers = 0b00000000000000000000000000010000, // 16
BanMembers = 0b00000000000000000000000000100000, // 32
// 6 bits of space
ChangeNickname = 0b00000000000000000001000000000000, // 4096
ManageNicknames = 0b00000000000000000010000000000000, // 8192
ChangeAvatar = 0b00000000000000000100000000000000, // 16382
RemoveAvatars = 0b00000000000000001000000000000000, // 32768
// 16 bits of space
}
lazy_static! {
pub static ref DEFAULT_PERMISSION: u32 =
ServerPermission::View
+ ServerPermission::ChangeNickname
+ ServerPermission::ChangeAvatar;
} }
impl_op_ex!(+ |a: &ServerPermission, b: &ServerPermission| -> u32 { *a as u32 | *b as u32 }); impl_op_ex!(+ |a: &ServerPermission, b: &ServerPermission| -> u32 { *a as u32 | *b as u32 });
...@@ -19,6 +37,16 @@ bitfield! { ...@@ -19,6 +37,16 @@ bitfield! {
pub struct ServerPermissions(MSB0 [u32]); pub struct ServerPermissions(MSB0 [u32]);
u32; u32;
pub get_view, _: 31; pub get_view, _: 31;
pub get_manage_roles, _: 30;
pub get_manage_channels, _: 29;
pub get_manage_server, _: 28;
pub get_kick_members, _: 27;
pub get_ban_members, _: 26;
pub get_change_nickname, _: 19;
pub get_manage_nicknames, _: 18;
pub get_change_avatar, _: 17;
pub get_remove_avatars, _: 16;
} }
impl<'a> PermissionCalculator<'a> { impl<'a> PermissionCalculator<'a> {
...@@ -29,14 +57,33 @@ impl<'a> PermissionCalculator<'a> { ...@@ -29,14 +57,33 @@ impl<'a> PermissionCalculator<'a> {
unreachable!() unreachable!()
}; };
if &self.perspective.id == server.owner { if self.perspective.id == server.owner {
Ok(u32::MAX) Ok(u32::MAX)
} else { } else {
Ok(ServerPermission::View as u32) match Ref::from_unchecked(self.perspective.id.clone()).fetch_member(&server.id).await {
Ok(member) => {
let mut perm = server.default_permissions.0 as u32;
if let Some(roles) = member.roles {
for role in roles {
if let Some(server_role) = server.roles.get(&role) {
perm |= server_role.permissions.0 as u32;
}
}
}
Ok(perm)
}
Err(error) => {
match &error {
Error::NotFound => Ok(0),
_ => Err(error)
}
}
}
} }
} }
pub async fn for_server(self) -> Result<ChannelPermissions<[u32; 1]>> { pub async fn for_server(self) -> Result<ServerPermissions<[u32; 1]>> {
Ok(ServerPermissions([self.calculate_server().await?])) Ok(ServerPermissions([self.calculate_server().await?]))
} }
} }
...@@ -10,10 +10,10 @@ use std::ops; ...@@ -10,10 +10,10 @@ use std::ops;
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, Copy, Clone)]
#[repr(u32)] #[repr(u32)]
pub enum UserPermission { pub enum UserPermission {
Access = 1, Access = 0b00000000000000000000000000000001, // 1
ViewProfile = 2, ViewProfile = 0b00000000000000000000000000000010, // 2
SendMessage = 4, SendMessage = 0b00000000000000000000000000000100, // 4
Invite = 8, Invite = 0b00000000000000000000000000001000, // 8
} }
bitfield! { bitfield! {
...@@ -30,7 +30,7 @@ impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 ...@@ -30,7 +30,7 @@ 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 {
if a.id == b { if a.id == b {
return RelationshipStatus::Friend; return RelationshipStatus::User;
} }
if let Some(relations) = &a.relations { if let Some(relations) = &a.relations {
...@@ -55,7 +55,7 @@ impl<'a> PermissionCalculator<'a> { ...@@ -55,7 +55,7 @@ impl<'a> PermissionCalculator<'a> {
.map(|v| v.to_owned()) .map(|v| v.to_owned())
.unwrap_or_else(|| get_relationship(&self.perspective, &target)) .unwrap_or_else(|| get_relationship(&self.perspective, &target))
{ {
RelationshipStatus::Friend => return Ok(u32::MAX), RelationshipStatus::Friend | RelationshipStatus::User => return Ok(u32::MAX),
RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => { RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
return Ok(UserPermission::Access as u32) return Ok(UserPermission::Access as u32)
} }
...@@ -64,12 +64,36 @@ impl<'a> PermissionCalculator<'a> { ...@@ -64,12 +64,36 @@ impl<'a> PermissionCalculator<'a> {
// ! INFO: if we add boolean switch for permission to // ! INFO: if we add boolean switch for permission to
// ! message people who have mutual, we need to get // ! message people who have mutual, we need to get
// ! rid of this return statement. // ! rid of this return statement.
return Ok(permissions); // return Ok(permissions);
} }
_ => {} _ => {}
} }
let check_server_overlap = async || {
let server_ids = User::fetch_server_ids(&self.perspective.id).await?;
Ok(
get_collection("server_members")
.find_one(
doc! {
"_id.user": &target,
"_id.server": {
"$in": server_ids
}
},
None
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find_one",
with: "server_members",
})?
.is_some()
)
};
if self.has_mutual_connection if self.has_mutual_connection
|| check_server_overlap().await?
|| get_collection("channels") || get_collection("channels")
.find_one( .find_one(
doc! { doc! {
...@@ -84,7 +108,7 @@ impl<'a> PermissionCalculator<'a> { ...@@ -84,7 +108,7 @@ impl<'a> PermissionCalculator<'a> {
) )
.await .await
.map_err(|_| Error::DatabaseError { .map_err(|_| Error::DatabaseError {
operation: "find", operation: "find_one",
with: "channels", with: "channels",
})? })?
.is_some() .is_some()
......
...@@ -86,14 +86,19 @@ async fn launch_web() { ...@@ -86,14 +86,19 @@ async fn launch_web() {
templates: Templates { templates: Templates {
verify_email: Template { verify_email: Template {
title: "Verify your REVOLT account.", title: "Verify your Revolt account.",
text: "Verify your email here: {{url}}", text: "You're almost there!
html: include_str!("../assets/templates/verify.html"), If you did not perform this action you can safely ignore this email.
Please verify your account here: {{url}}",
html: None,
}, },
reset_password: Template { reset_password: Template {
title: "Reset your REVOLT password.", title: "Reset your Revolt password.",
text: "Reset your password here: {{url}}", text: "You requested a password reset, if you did not perform this action you can safely ignore this email.
html: include_str!("../assets/templates/reset.html"),
Reset your password here: {{url}}",
html: None,
}, },
welcome: None, welcome: None,
}, },
......
use std::collections::HashMap;
use hive_pubsub::PubSub; use hive_pubsub::PubSub;
use mongodb::bson::doc;
use rauth::auth::Session; use rauth::auth::Session;
use rocket_contrib::json::JsonValue; use rocket_contrib::json::JsonValue;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::hive::{get_hive, subscribe_if_exists}; use super::hive::{get_hive, subscribe_if_exists};
use crate::database::*; use crate::{database::*, util::result::Result};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "error")] #[serde(tag = "error")]
pub enum WebSocketError { pub enum WebSocketError {
LabelMe, LabelMe,
...@@ -26,7 +25,7 @@ pub enum ServerboundNotification { ...@@ -26,7 +25,7 @@ pub enum ServerboundNotification {
EndTyping { channel: String }, EndTyping { channel: String },
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RemoveUserField { pub enum RemoveUserField {
ProfileContent, ProfileContent,
ProfileBackground, ProfileBackground,
...@@ -34,18 +33,31 @@ pub enum RemoveUserField { ...@@ -34,18 +33,31 @@ pub enum RemoveUserField {
Avatar, Avatar,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RemoveChannelField { pub enum RemoveChannelField {
Icon, Icon,
Description
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RemoveServerField { pub enum RemoveServerField {
Icon, Icon,
Banner, Banner,
Description,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RemoveRoleField {
Colour,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum RemoveMemberField {
Nickname,
Avatar,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum ClientboundNotification { pub enum ClientboundNotification {
Error(WebSocketError), Error(WebSocketError),
...@@ -54,6 +66,7 @@ pub enum ClientboundNotification { ...@@ -54,6 +66,7 @@ pub enum ClientboundNotification {
users: Vec<User>, users: Vec<User>,
servers: Vec<Server>, servers: Vec<Server>,
channels: Vec<Channel>, channels: Vec<Channel>,
members: Vec<Member>
}, },
Message(Message), Message(Message),
...@@ -93,8 +106,12 @@ pub enum ClientboundNotification { ...@@ -93,8 +106,12 @@ pub enum ClientboundNotification {
id: String, id: String,
user: String, user: String,
}, },
ChannelAck {
id: String,
user: String,
message_id: String,
},
ServerCreate(Server),
ServerUpdate { ServerUpdate {
id: String, id: String,
data: JsonValue, data: JsonValue,
...@@ -104,6 +121,12 @@ pub enum ClientboundNotification { ...@@ -104,6 +121,12 @@ pub enum ClientboundNotification {
ServerDelete { ServerDelete {
id: String, id: String,
}, },
ServerMemberUpdate {
id: MemberCompositeKey,
data: JsonValue,
#[serde(skip_serializing_if = "Option::is_none")]
clear: Option<RemoveMemberField>,
},
ServerMemberJoin { ServerMemberJoin {
id: String, id: String,
user: String, user: String,
...@@ -112,6 +135,17 @@ pub enum ClientboundNotification { ...@@ -112,6 +135,17 @@ pub enum ClientboundNotification {
id: String, id: String,
user: String, user: String,
}, },
ServerRoleUpdate {
id: String,
role_id: String,
data: JsonValue,
#[serde(skip_serializing_if = "Option::is_none")]
clear: Option<RemoveRoleField>
},
ServerRoleDelete {
id: String,
role_id: String
},
UserUpdate { UserUpdate {
id: String, id: String,
...@@ -124,10 +158,6 @@ pub enum ClientboundNotification { ...@@ -124,10 +158,6 @@ pub enum ClientboundNotification {
user: User, user: User,
status: RelationshipStatus, status: RelationshipStatus,
}, },
UserPresence {
id: String,
online: bool,
},
UserSettingsUpdate { UserSettingsUpdate {
id: String, id: String,
update: JsonValue, update: JsonValue,
...@@ -137,15 +167,27 @@ pub enum ClientboundNotification { ...@@ -137,15 +167,27 @@ pub enum ClientboundNotification {
impl ClientboundNotification { impl ClientboundNotification {
pub fn publish(self, topic: String) { pub fn publish(self, topic: String) {
async_std::task::spawn(async move { async_std::task::spawn(async move {
prehandle_hook(&self); // ! TODO: this should be moved to pubsub prehandle_hook(&self).await.ok(); // ! FIXME: this should be moved to pubsub
hive_pubsub::backend::mongo::publish(get_hive(), &topic, self) hive_pubsub::backend::mongo::publish(get_hive(), &topic, self)
.await .await
.ok(); .ok();
}); });
} }
pub fn publish_as_user(self, user: String) {
self.clone().publish(user.clone());
async_std::task::spawn(async move {
if let Ok(server_ids) = User::fetch_server_ids(&user).await {
for server in server_ids {
self.clone().publish(server.clone());
}
}
});
}
} }
pub fn prehandle_hook(notification: &ClientboundNotification) { pub async fn prehandle_hook(notification: &ClientboundNotification) -> Result<()> {
match &notification { match &notification {
ClientboundNotification::ChannelGroupJoin { id, user } => { ClientboundNotification::ChannelGroupJoin { id, user } => {
subscribe_if_exists(user.clone(), id.clone()).ok(); subscribe_if_exists(user.clone(), id.clone()).ok();
...@@ -161,13 +203,24 @@ pub fn prehandle_hook(notification: &ClientboundNotification) { ...@@ -161,13 +203,24 @@ pub fn prehandle_hook(notification: &ClientboundNotification) {
subscribe_if_exists(recipient.clone(), channel_id.to_string()).ok(); subscribe_if_exists(recipient.clone(), channel_id.to_string()).ok();
} }
} }
Channel::TextChannel { server, .. }
| Channel::VoiceChannel { server, .. } => {
// ! FIXME: write a better algorithm?
let members = Server::fetch_member_ids(server).await?;
for member in members {
subscribe_if_exists(member.clone(), channel_id.to_string()).ok();
}
}
} }
} }
ClientboundNotification::ServerMemberJoin { id, user } => { ClientboundNotification::ServerMemberJoin { id, user } => {
let server = Ref::from_unchecked(id.clone()).fetch_server().await?;
subscribe_if_exists(user.clone(), id.clone()).ok(); subscribe_if_exists(user.clone(), id.clone()).ok();
}
ClientboundNotification::ServerCreate(server) => { for channel in server.channels {
subscribe_if_exists(server.owner.clone(), server.id.clone()).ok(); subscribe_if_exists(user.clone(), channel).ok();
}
} }
ClientboundNotification::UserRelationship { id, user, status } => { ClientboundNotification::UserRelationship { id, user, status } => {
if status != &RelationshipStatus::None { if status != &RelationshipStatus::None {
...@@ -176,34 +229,33 @@ pub fn prehandle_hook(notification: &ClientboundNotification) { ...@@ -176,34 +229,33 @@ pub fn prehandle_hook(notification: &ClientboundNotification) {
} }
_ => {} _ => {}
} }
Ok(())
} }
pub fn posthandle_hook(notification: &ClientboundNotification) { pub async fn posthandle_hook(notification: &ClientboundNotification) {
match &notification { match &notification {
ClientboundNotification::ChannelDelete { id } => { ClientboundNotification::ChannelDelete { id } => {
get_hive().hive.drop_topic(&id).ok(); get_hive().hive.drop_topic(&id).ok();
} }
ClientboundNotification::ChannelGroupLeave { id, user } => { ClientboundNotification::ChannelGroupLeave { id, user } => {
get_hive() get_hive().hive.unsubscribe(user, id).ok();
.hive
.unsubscribe(&user.to_string(), &id.to_string())
.ok();
} }
ClientboundNotification::ServerDelete { id } => { ClientboundNotification::ServerDelete { id } => {
get_hive().hive.drop_topic(&id).ok(); get_hive().hive.drop_topic(&id).ok();
} }
ClientboundNotification::ServerMemberLeave { id, user } => { ClientboundNotification::ServerMemberLeave { id, user } => {
get_hive() get_hive().hive.unsubscribe(user, id).ok();
.hive
.unsubscribe(&user.to_string(), &id.to_string()) if let Ok(server) = Ref::from_unchecked(id.clone()).fetch_server().await {
.ok(); for channel in server.channels {
get_hive().hive.unsubscribe(user, &channel).ok();
}
}
} }
ClientboundNotification::UserRelationship { id, user, status } => { ClientboundNotification::UserRelationship { id, user, status } => {
if status == &RelationshipStatus::None { if status == &RelationshipStatus::None {
get_hive() get_hive().hive.unsubscribe(id, &user.id).ok();
.hive
.unsubscribe(&id.to_string(), &user.id.to_string())
.ok();
} }
} }
_ => {} _ => {}
......
...@@ -13,8 +13,11 @@ static HIVE: OnceCell<Hive> = OnceCell::new(); ...@@ -13,8 +13,11 @@ static HIVE: OnceCell<Hive> = OnceCell::new();
pub async fn init_hive() { pub async fn init_hive() {
let hive = MongodbPubSub::new( let hive = MongodbPubSub::new(
|ids, notification| { |ids, notification: ClientboundNotification| {
super::events::posthandle_hook(&notification); let notif = notification.clone();
async_std::task::spawn(async move {
super::events::posthandle_hook(&notif).await;
});
if let Ok(data) = to_string(&notification) { if let Ok(data) = to_string(&notification) {
debug!("Pushing out notification. {}", data); debug!("Pushing out notification. {}", data);
......
...@@ -6,7 +6,7 @@ use crate::{ ...@@ -6,7 +6,7 @@ use crate::{
util::result::{Error, Result}, util::result::{Error, Result},
}; };
use futures::StreamExt; use futures::StreamExt;
use mongodb::bson::{doc, from_document, Document}; use mongodb::bson::{doc, from_document};
pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
let mut user_ids: HashSet<String> = HashSet::new(); let mut user_ids: HashSet<String> = HashSet::new();
...@@ -19,30 +19,11 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { ...@@ -19,30 +19,11 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
); );
} }
let server_ids = get_collection("server_members") let members = User::fetch_memberships(&user.id).await?;
.find( let server_ids: Vec<String> = members.iter()
doc! { .map(|x| x.id.server.clone())
"_id.user": &user.id .collect();
},
None,
)
.await
.map_err(|_| Error::DatabaseError {
operation: "find",
with: "server_members",
})?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| {
x.get_document("_id")
.ok()
.map(|i| i.get_str("server").ok().map(|x| x.to_string()))
})
.flatten()
.collect::<Vec<String>>();
let mut cursor = get_collection("servers") let mut cursor = get_collection("servers")
.find( .find(
doc! { doc! {
...@@ -137,5 +118,6 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> { ...@@ -137,5 +118,6 @@ pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
users, users,
servers, servers,
channels, channels,
members
}) })
} }
...@@ -4,6 +4,7 @@ use super::hive::get_hive; ...@@ -4,6 +4,7 @@ use super::hive::get_hive;
use futures::StreamExt; use futures::StreamExt;
use hive_pubsub::PubSub; use hive_pubsub::PubSub;
use mongodb::bson::doc; use mongodb::bson::doc;
use mongodb::bson::Document;
use mongodb::options::FindOptions; use mongodb::options::FindOptions;
pub async fn generate_subscriptions(user: &User) -> Result<(), String> { pub async fn generate_subscriptions(user: &User) -> Result<(), String> {
...@@ -16,6 +17,43 @@ pub async fn generate_subscriptions(user: &User) -> Result<(), String> { ...@@ -16,6 +17,43 @@ pub async fn generate_subscriptions(user: &User) -> Result<(), String> {
} }
} }
let server_ids = User::fetch_server_ids(&user.id)
.await
.map_err(|_| "Failed to fetch memberships.".to_string())?;
let channel_ids = get_collection("servers")
.find(
doc! {
"_id": {
"$in": &server_ids
}
},
None,
)
.await
.map_err(|_| "Failed to fetch servers.".to_string())?
.filter_map(async move |s| s.ok())
.collect::<Vec<Document>>()
.await
.into_iter()
.filter_map(|x| {
x.get_array("channels").ok().map(|v| {
v.into_iter()
.filter_map(|x| x.as_str().map(|x| x.to_string()))
.collect::<Vec<String>>()
})
})
.flatten()
.collect::<Vec<String>>();
for id in server_ids {
hive.subscribe(user.id.clone(), id)?;
}
for id in channel_ids {
hive.subscribe(user.id.clone(), id)?;
}
let mut cursor = get_collection("channels") let mut cursor = get_collection("channels")
.find( .find(
doc! { doc! {
...@@ -45,7 +83,5 @@ pub async fn generate_subscriptions(user: &User) -> Result<(), String> { ...@@ -45,7 +83,5 @@ pub async fn generate_subscriptions(user: &User) -> Result<(), String> {
} }
} }
// ! FIXME: fetch memberships for servers
Ok(()) Ok(())
} }
...@@ -129,11 +129,14 @@ async fn accept(stream: TcpStream) { ...@@ -129,11 +129,14 @@ async fn accept(stream: TcpStream) {
send(payload); send(payload);
if !was_online { if !was_online {
ClientboundNotification::UserPresence { ClientboundNotification::UserUpdate {
id: id.clone(), id: id.clone(),
online: true, data: json!({
"online": true
}),
clear: None
} }
.publish(id); .publish_as_user(id);
} }
} }
Err(_) => { Err(_) => {
...@@ -228,11 +231,14 @@ async fn accept(stream: TcpStream) { ...@@ -228,11 +231,14 @@ async fn accept(stream: TcpStream) {
} }
if let Some(id) = offline { if let Some(id) = offline {
ClientboundNotification::UserPresence { ClientboundNotification::UserUpdate {
id: id.clone(), id: id.clone(),
online: false, data: json!({
"online": false
}),
clear: None
} }
.publish(id); .publish_as_user(id);
} }
} }
...@@ -244,7 +250,8 @@ pub fn publish(ids: Vec<String>, notification: ClientboundNotification) { ...@@ -244,7 +250,8 @@ pub fn publish(ids: Vec<String>, notification: ClientboundNotification) {
// Block certain notifications from reaching users that aren't meant to see them. // Block certain notifications from reaching users that aren't meant to see them.
match &notification { match &notification {
ClientboundNotification::UserRelationship { id: user_id, .. } ClientboundNotification::UserRelationship { id: user_id, .. }
| ClientboundNotification::UserSettingsUpdate { id: user_id, .. } => { | ClientboundNotification::UserSettingsUpdate { id: user_id, .. }
| ClientboundNotification::ChannelAck { user: user_id, .. } => {
if &id != user_id { if &id != user_id {
continue; continue;
} }
......
use crate::database::*;
use crate::notifications::events::ClientboundNotification;
use crate::util::result::{Error, Result};
use mongodb::bson::doc;
use mongodb::options::UpdateOptions;
#[put("/<target>/ack/<message>")]
pub async fn req(user: User, target: Ref, message: Ref) -> Result<()> {
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_view() {
Err(Error::MissingPermission)?
}
let id = target.id();
get_collection("channel_unreads")
.update_one(
doc! {
"_id.channel": id,
"_id.user": &user.id
},
doc! {
"$unset": {
"mentions": 1
},
"$set": {
"last_id": &message.id
}
},
UpdateOptions::builder().upsert(true).build(),
)
.await
.map_err(|_| Error::DatabaseError {
operation: "update_one",
with: "channel_unreads",
})?;
ClientboundNotification::ChannelAck {
id: id.to_string(),
user: user.id.clone(),
message_id: message.id,
}
.publish(user.id);
Ok(())
}
...@@ -11,6 +11,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> { ...@@ -11,6 +11,7 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
.with_channel(&target) .with_channel(&target)
.for_channel() .for_channel()
.await?; .await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::MissingPermission)? Err(Error::MissingPermission)?
} }
...@@ -97,16 +98,20 @@ pub async fn req(user: User, target: Ref) -> Result<()> { ...@@ -97,16 +98,20 @@ pub async fn req(user: User, target: Ref) -> Result<()> {
} }
.publish(id.clone()); .publish(id.clone());
Message::create( Content::SystemMessage(SystemMessage::UserLeft { id: user.id })
"00000000000000000000000000".to_string(), .send_as_system(&target)
id.clone(), .await
Content::SystemMessage(SystemMessage::UserLeft { id: user.id }), .ok();
)
.publish(&target)
.await
.ok();
Ok(()) Ok(())
} }
Channel::TextChannel { .. } |
Channel::VoiceChannel { .. } => {
if perm.get_manage_channel() {
target.delete().await
} else {
Err(Error::MissingPermission)
}
}
} }
} }
...@@ -45,7 +45,9 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { ...@@ -45,7 +45,9 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
} }
match &target { match &target {
Channel::Group { id, icon, .. } => { Channel::Group { id, icon, .. }
| Channel::TextChannel { id, icon, .. }
| Channel::VoiceChannel { id, icon, .. } => {
let mut set = doc! {}; let mut set = doc! {};
let mut unset = doc! {}; let mut unset = doc! {};
...@@ -56,6 +58,9 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { ...@@ -56,6 +58,9 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
unset.insert("icon", 1); unset.insert("icon", 1);
remove_icon = true; remove_icon = true;
} }
RemoveChannelField::Description => {
unset.insert("description", 1);
}
} }
} }
...@@ -69,7 +74,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { ...@@ -69,7 +74,7 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
if let Some(attachment_id) = &data.icon { if let Some(attachment_id) = &data.icon {
let attachment = let attachment =
File::find_and_use(&attachment_id, "icons", "object", &user.id).await?; File::find_and_use(&attachment_id, "icons", "object", target.id()).await?;
set.insert( set.insert(
"icon", "icon",
to_document(&attachment).map_err(|_| Error::DatabaseError { to_document(&attachment).map_err(|_| Error::DatabaseError {
...@@ -107,42 +112,32 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> { ...@@ -107,42 +112,32 @@ pub async fn req(user: User, target: Ref, data: Json<Data>) -> Result<()> {
} }
.publish(id.clone()); .publish(id.clone());
if let Some(name) = data.name { if let Channel::Group { .. } = &target {
Message::create( if let Some(name) = data.name {
"00000000000000000000000000".to_string(),
id.clone(),
Content::SystemMessage(SystemMessage::ChannelRenamed { Content::SystemMessage(SystemMessage::ChannelRenamed {
name, name,
by: user.id.clone(), by: user.id.clone(),
}), })
) .send_as_system(&target)
.publish(&target) .await
.await .ok();
.ok(); }
}
if let Some(_) = data.description { if let Some(_) = data.description {
Message::create(
"00000000000000000000000000".to_string(),
id.clone(),
Content::SystemMessage(SystemMessage::ChannelDescriptionChanged { Content::SystemMessage(SystemMessage::ChannelDescriptionChanged {
by: user.id.clone(), by: user.id.clone(),
}), })
) .send_as_system(&target)
.publish(&target) .await
.await .ok();
.ok(); }
}
if let Some(_) = data.icon { if let Some(_) = data.icon {
Message::create( Content::SystemMessage(SystemMessage::ChannelIconChanged { by: user.id })
"00000000000000000000000000".to_string(), .send_as_system(&target)
id.clone(), .await
Content::SystemMessage(SystemMessage::ChannelIconChanged { by: user.id }), .ok();
) }
.publish(&target)
.await
.ok();
} }
if remove_icon { if remove_icon {
......
...@@ -11,6 +11,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> { ...@@ -11,6 +11,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
.with_channel(&target) .with_channel(&target)
.for_channel() .for_channel()
.await?; .await?;
if !perm.get_view() { if !perm.get_view() {
Err(Error::MissingPermission)? Err(Error::MissingPermission)?
} }
......
...@@ -15,7 +15,8 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { ...@@ -15,7 +15,8 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
.with_channel(&channel) .with_channel(&channel)
.for_channel() .for_channel()
.await?; .await?;
if !perm.get_view() {
if !perm.get_invite_others() {
Err(Error::MissingPermission)? Err(Error::MissingPermission)?
} }
...@@ -54,15 +55,11 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { ...@@ -54,15 +55,11 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
} }
.publish(id.clone()); .publish(id.clone());
Message::create( Content::SystemMessage(SystemMessage::UserAdded {
"00000000000000000000000000".to_string(), id: member.id,
id.clone(), by: user.id,
Content::SystemMessage(SystemMessage::UserAdded { })
id: member.id, .send_as_system(&channel)
by: user.id,
}),
)
.publish(&channel)
.await .await
.ok(); .ok();
......
...@@ -24,6 +24,7 @@ pub struct Data { ...@@ -24,6 +24,7 @@ pub struct Data {
#[post("/create", data = "<info>")] #[post("/create", data = "<info>")]
pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> { pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> {
let info = info.into_inner();
info.validate() info.validate()
.map_err(|error| Error::FailedValidation { error })?; .map_err(|error| Error::FailedValidation { error })?;
...@@ -37,8 +38,11 @@ pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> { ...@@ -37,8 +38,11 @@ pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> {
} }
for target in &set { for target in &set {
if get_relationship(&user, target) != RelationshipStatus::Friend { match get_relationship(&user, target) {
Err(Error::NotFriends)? RelationshipStatus::Friend | RelationshipStatus::User => {}
_ => {
return Err(Error::NotFriends);
}
} }
} }
...@@ -62,16 +66,14 @@ pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> { ...@@ -62,16 +66,14 @@ pub async fn req(user: User, info: Json<Data>) -> Result<JsonValue> {
let id = Ulid::new().to_string(); let id = Ulid::new().to_string();
let channel = Channel::Group { let channel = Channel::Group {
id, id,
nonce: Some(info.nonce.clone()), nonce: Some(info.nonce),
name: info.name.clone(), name: info.name,
description: info description: info.description,
.description
.clone()
.unwrap_or_else(|| "A group.".to_string()),
owner: user.id, owner: user.id,
recipients: set.into_iter().collect::<Vec<String>>(), recipients: set.into_iter().collect::<Vec<String>>(),
icon: None, icon: None,
last_message: None, last_message: None,
permissions: None
}; };
channel.clone().publish().await?; channel.clone().publish().await?;
......
...@@ -51,15 +51,11 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> { ...@@ -51,15 +51,11 @@ pub async fn req(user: User, target: Ref, member: Ref) -> Result<()> {
} }
.publish(id.clone()); .publish(id.clone());
Message::create( Content::SystemMessage(SystemMessage::UserRemove {
"00000000000000000000000000".to_string(), id: member.id,
id.clone(), by: user.id,
Content::SystemMessage(SystemMessage::UserRemove { })
id: member.id, .send_as_system(&channel)
by: user.id,
}),
)
.publish(&channel)
.await .await
.ok(); .ok();
......
use crate::database::*;
use crate::util::result::{Error, Result};
use mongodb::bson::doc;
use nanoid::nanoid;
use rocket_contrib::json::JsonValue;
lazy_static! {
static ref ALPHABET: [char; 54] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'J', 'K', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'
];
}
#[post("/<target>/invites")]
pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
let target = target.fetch_channel().await?;
let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&target)
.for_channel()
.await?;
if !perm.get_invite_others() {
return Err(Error::MissingPermission);
}
let code = nanoid!(8, &*ALPHABET);
match &target {
Channel::Group { .. } => {
unimplemented!()
}
Channel::TextChannel { id, server, .. }
| Channel::VoiceChannel { id, server, .. } => {
Invite::Server {
code: code.clone(),
creator: user.id,
server: server.clone(),
channel: id.clone(),
}
.save()
.await?;
Ok(json!({ "code": code }))
}
_ => Err(Error::InvalidOperation),
}
}
...@@ -6,6 +6,7 @@ use mongodb::bson::doc; ...@@ -6,6 +6,7 @@ use mongodb::bson::doc;
#[delete("/<target>/messages/<msg>")] #[delete("/<target>/messages/<msg>")]
pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> { pub async fn req(user: User, target: Ref, msg: Ref) -> Result<()> {
let channel = target.fetch_channel().await?; let channel = target.fetch_channel().await?;
channel.has_messaging()?;
let perm = permissions::PermissionCalculator::new(&user) let perm = permissions::PermissionCalculator::new(&user)
.with_channel(&channel) .with_channel(&channel)
......