diff --git a/crates/core/permissions/Cargo.toml b/crates/core/permissions/Cargo.toml new file mode 100644 index 00000000..5fb3c071 --- /dev/null +++ b/crates/core/permissions/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "revolt-permissions" +version = "0.0.1" +edition = "2021" +license = "AGPL-3.0-or-later" +authors = [ "Paul Makles " ] +description = "Revolt Backend: Permission Logic" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +serde = [ "dep:serde" ] +schemas = [ "dep:schemars" ] +try-from-primitive = [ "dep:num_enum" ] + +[dev-dependencies] +# Async +async-std = { version = "1.8.0", features = ["attributes"] } + +[dependencies] +# Utility +auto_ops = "0.3.0" +once_cell = "1.17" +num_enum = { version = "0.6.1", optional = true } + +# Async +async-trait = "0.1.51" + +# Serialisation +serde = { version = "1", features = ["derive"], optional = true } + +# Spec Generation +schemars = { version = "0.8.8", optional = true } diff --git a/crates/core/permissions/src/impl.rs b/crates/core/permissions/src/impl.rs new file mode 100644 index 00000000..11c378c6 --- /dev/null +++ b/crates/core/permissions/src/impl.rs @@ -0,0 +1,128 @@ +use crate::{ + ChannelPermission, ChannelType, PermissionQuery, PermissionValue, RelationshipStatus, + UserPermission, ALLOW_IN_TIMEOUT, DEFAULT_PERMISSION_DIRECT_MESSAGE, + DEFAULT_PERMISSION_SAVED_MESSAGES, DEFAULT_PERMISSION_VIEW_ONLY, +}; + +/// Calculate permissions against a user +pub async fn calculate_user_permissions(query: &mut P) -> u32 { + if query.are_we_privileged().await { + return u32::MAX; + } + + if query.are_the_users_same().await { + return u32::MAX; + } + + let mut permissions = 0_u32; + match query.user_relationship().await { + RelationshipStatus::Friend => return u32::MAX, + RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => { + return UserPermission::Access as u32 + } + RelationshipStatus::Incoming | RelationshipStatus::Outgoing => { + permissions = UserPermission::Access as u32; + } + _ => {} + } + + if query.have_mutual_connection().await { + permissions = UserPermission::Access + UserPermission::ViewProfile; + + if query.user_is_bot().await || query.are_we_a_bot().await { + permissions += UserPermission::SendMessage as u32; + } + + permissions + } else { + permissions + } +} + +/// Calculate permissions against a server +pub async fn calculate_server_permissions(query: &mut P) -> PermissionValue { + if query.are_we_privileged().await || query.are_we_server_owner().await { + return ChannelPermission::GrantAllSafe.into(); + } + + if !query.are_we_a_member().await { + return 0_u64.into(); + } + + let mut permissions: PermissionValue = query.get_default_server_permissions().await.into(); + + for role_override in query.get_our_server_role_overrides().await { + permissions.apply(role_override); + } + + if query.are_we_timed_out().await { + permissions.restrict(*ALLOW_IN_TIMEOUT); + } + + permissions +} + +/// Calculate permissions against a channel +pub async fn calculate_channel_permissions(query: &mut P) -> PermissionValue { + if query.are_we_privileged().await { + return ChannelPermission::GrantAllSafe.into(); + } + + match query.get_channel_type().await { + ChannelType::SavedMessages => { + if query.do_we_own_the_channel().await { + DEFAULT_PERMISSION_SAVED_MESSAGES.into() + } else { + 0_u64.into() + } + } + ChannelType::DirectMessage => { + if query.are_we_part_of_the_channel().await { + query.set_recipient_as_user().await; + + let permissions = calculate_user_permissions(query).await; + if (permissions & UserPermission::SendMessage as u32) + == UserPermission::SendMessage as u32 + { + (*DEFAULT_PERMISSION_DIRECT_MESSAGE).into() + } else { + (*DEFAULT_PERMISSION_VIEW_ONLY).into() + } + } else { + 0_u64.into() + } + } + ChannelType::Group => { + if query.do_we_own_the_channel().await { + ChannelPermission::GrantAllSafe.into() + } else if query.are_we_part_of_the_channel().await { + (*DEFAULT_PERMISSION_VIEW_ONLY + | query.get_default_channel_permissions().await.allow) + .into() + } else { + 0_u64.into() + } + } + ChannelType::ServerChannel => { + query.set_server_from_channel().await; + + if query.are_we_a_member().await { + let mut permissions = calculate_server_permissions(query).await; + permissions.apply(query.get_default_channel_permissions().await); + + for role_override in query.get_our_channel_role_overrides().await { + permissions.apply(role_override); + } + + if query.are_we_timed_out().await { + permissions.restrict(*ALLOW_IN_TIMEOUT); + } + + permissions + } else { + 0_u64.into() + } + } + ChannelType::Unknown => 0_u64.into(), + } +} diff --git a/crates/core/permissions/src/lib.rs b/crates/core/permissions/src/lib.rs new file mode 100644 index 00000000..734d7be1 --- /dev/null +++ b/crates/core/permissions/src/lib.rs @@ -0,0 +1,16 @@ +#[macro_use] +extern crate auto_ops; + +#[macro_use] +extern crate async_trait; + +mod r#impl; +mod models; +mod r#trait; + +pub use models::*; +pub use r#impl::*; +pub use r#trait::*; + +#[cfg(test)] +mod test; diff --git a/crates/core/permissions/src/models/channel.rs b/crates/core/permissions/src/models/channel.rs new file mode 100644 index 00000000..25dd81db --- /dev/null +++ b/crates/core/permissions/src/models/channel.rs @@ -0,0 +1,137 @@ +use once_cell::sync::Lazy; +use std::ops::Add; + +/// Abstract channel type +pub enum ChannelType { + SavedMessages, + DirectMessage, + Group, + ServerChannel, + Unknown, +} + +/// Permission value on Revolt +/// +/// This should be restricted to the lower 52 bits to prevent any +/// potential issues with Javascript. Also leave empty spaces for +/// future permission flags to be added. +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "try-from-primitive", derive(num_enum::TryFromPrimitive))] +#[repr(u64)] +pub enum ChannelPermission { + // * Generic permissions + /// Manage the channel or channels on the server + ManageChannel = 1 << 0, + /// Manage the server + ManageServer = 1 << 1, + /// Manage permissions on servers or channels + ManagePermissions = 1 << 2, + /// Manage roles on server + ManageRole = 1 << 3, + /// Manage server customisation (includes emoji) + ManageCustomisation = 1 << 4, + + // % 1 bit reserved + + // * Member permissions + /// Kick other members below their ranking + KickMembers = 1 << 6, + /// Ban other members below their ranking + BanMembers = 1 << 7, + /// Timeout other members below their ranking + TimeoutMembers = 1 << 8, + /// Assign roles to members below their ranking + AssignRoles = 1 << 9, + /// Change own nickname + ChangeNickname = 1 << 10, + /// Change or remove other's nicknames below their ranking + ManageNicknames = 1 << 11, + /// Change own avatar + ChangeAvatar = 1 << 12, + /// Remove other's avatars below their ranking + RemoveAvatars = 1 << 13, + + // % 7 bits reserved + + // * Channel permissions + /// View a channel + ViewChannel = 1 << 20, + /// Read a channel's past message history + ReadMessageHistory = 1 << 21, + /// Send a message in a channel + SendMessage = 1 << 22, + /// Delete messages in a channel + ManageMessages = 1 << 23, + /// Manage webhook entries on a channel + ManageWebhooks = 1 << 24, + /// Create invites to this channel + InviteOthers = 1 << 25, + /// Send embedded content in this channel + SendEmbeds = 1 << 26, + /// Send attachments and media in this channel + UploadFiles = 1 << 27, + /// Masquerade messages using custom nickname and avatar + Masquerade = 1 << 28, + /// React to messages with emojis + React = 1 << 29, + + // * Voice permissions + /// Connect to a voice channel + Connect = 1 << 30, + /// Speak in a voice call + Speak = 1 << 31, + /// Share video in a voice call + Video = 1 << 32, + /// Mute other members with lower ranking in a voice call + MuteMembers = 1 << 33, + /// Deafen other members with lower ranking in a voice call + DeafenMembers = 1 << 34, + /// Move members between voice channels + MoveMembers = 1 << 35, + + // * Misc. permissions + // % Bits 36 to 52: free area + // % Bits 53 to 64: do not use + + // * Grant all permissions + /// Safely grant all permissions + GrantAllSafe = 0x000F_FFFF_FFFF_FFFF, + + /// Grant all permissions + GrantAll = u64::MAX, +} + +impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u64 { *a as u64 | *b as u64 }); +impl_op_ex_commutative!(+ |a: &u64, b: &ChannelPermission| -> u64 { *a | *b as u64 }); + +pub static ALLOW_IN_TIMEOUT: Lazy = + Lazy::new(|| ChannelPermission::ViewChannel + ChannelPermission::ReadMessageHistory); + +pub static DEFAULT_PERMISSION_VIEW_ONLY: Lazy = + Lazy::new(|| ChannelPermission::ViewChannel + ChannelPermission::ReadMessageHistory); + +pub static DEFAULT_PERMISSION: Lazy = Lazy::new(|| { + DEFAULT_PERMISSION_VIEW_ONLY.add( + ChannelPermission::SendMessage + + ChannelPermission::InviteOthers + + ChannelPermission::SendEmbeds + + ChannelPermission::UploadFiles + + ChannelPermission::Connect + + ChannelPermission::Speak, + ) +}); + +pub static DEFAULT_PERMISSION_SAVED_MESSAGES: u64 = ChannelPermission::GrantAllSafe as u64; + +pub static DEFAULT_PERMISSION_DIRECT_MESSAGE: Lazy = Lazy::new(|| { + DEFAULT_PERMISSION.add(ChannelPermission::ManageChannel + ChannelPermission::React) +}); + +pub static DEFAULT_PERMISSION_SERVER: Lazy = Lazy::new(|| { + DEFAULT_PERMISSION.add( + ChannelPermission::React + + ChannelPermission::ChangeNickname + + ChannelPermission::ChangeAvatar, + ) +}); diff --git a/crates/core/permissions/src/models/mod.rs b/crates/core/permissions/src/models/mod.rs new file mode 100644 index 00000000..e234eac5 --- /dev/null +++ b/crates/core/permissions/src/models/mod.rs @@ -0,0 +1,58 @@ +mod channel; +mod server; +mod user; + +pub use channel::*; +pub use server::*; +pub use user::*; + +/// Holds a permission value to manipulate. +#[derive(Debug)] +pub struct PermissionValue(u64); + +impl PermissionValue { + /// Apply a given override to this value + pub fn apply(&mut self, v: Override) { + self.allow(v.allow); + self.revoke(v.deny); + } + + /// Allow given permissions + pub fn allow(&mut self, v: u64) { + self.0 |= v; + } + + /// Revoke given permissions + pub fn revoke(&mut self, v: u64) { + self.0 &= !v; + } + + /// Restrict to given permissions + pub fn restrict(&mut self, v: u64) { + self.0 &= v; + } +} + +impl From for PermissionValue { + fn from(v: i64) -> Self { + Self(v as u64) + } +} + +impl From for PermissionValue { + fn from(v: u64) -> Self { + Self(v) + } +} + +impl From for u64 { + fn from(v: PermissionValue) -> Self { + v.0 + } +} + +impl From for PermissionValue { + fn from(v: ChannelPermission) -> Self { + (v as u64).into() + } +} diff --git a/crates/core/permissions/src/models/server.rs b/crates/core/permissions/src/models/server.rs new file mode 100644 index 00000000..08ae8443 --- /dev/null +++ b/crates/core/permissions/src/models/server.rs @@ -0,0 +1,56 @@ +/// Representation of a single permission override +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Override { + /// Allow bit flags + pub allow: u64, + /// Disallow bit flags + pub deny: u64, +} + +/// Representation of a single permission override +/// as it appears on models and in the database +#[derive(/*JsonSchema, */ Debug, Clone, Copy, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct OverrideField { + /// Allow bit flags + a: i64, + /// Disallow bit flags + d: i64, +} + +impl Override { + /// Into allows + pub fn allows(&self) -> u64 { + self.allow + } + + /// Into denies + pub fn denies(&self) -> u64 { + self.deny + } +} + +impl From for OverrideField { + fn from(v: Override) -> Self { + Self { + a: v.allow as i64, + d: v.deny as i64, + } + } +} + +impl From for Override { + fn from(v: OverrideField) -> Self { + Self { + allow: v.a as u64, + deny: v.d as u64, + } + } +} + +/*impl From for Bson { + fn from(v: OverrideField) -> Self { + Self::Document(bson::to_document(&v).unwrap()) + } +}*/ diff --git a/crates/core/permissions/src/models/user.rs b/crates/core/permissions/src/models/user.rs new file mode 100644 index 00000000..80a8da8f --- /dev/null +++ b/crates/core/permissions/src/models/user.rs @@ -0,0 +1,25 @@ +/// User's relationship with another user (or themselves) +pub enum RelationshipStatus { + None, + User, + Friend, + Outgoing, + Incoming, + Blocked, + BlockedOther, +} + +/// User permission definitions +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "try-from-primitive", derive(num_enum::TryFromPrimitive))] +#[repr(u32)] +pub enum UserPermission { + Access = 1 << 0, + ViewProfile = 1 << 1, + SendMessage = 1 << 2, + Invite = 1 << 3, +} + +impl_op_ex!(+ |a: &UserPermission, b: &UserPermission| -> u32 { *a as u32 | *b as u32 }); +impl_op_ex_commutative!(+ |a: &u32, b: &UserPermission| -> u32 { *a | *b as u32 }); diff --git a/crates/core/permissions/src/test.rs b/crates/core/permissions/src/test.rs new file mode 100644 index 00000000..d58e8919 --- /dev/null +++ b/crates/core/permissions/src/test.rs @@ -0,0 +1,377 @@ +use crate::{ + calculate_channel_permissions, calculate_user_permissions, ChannelPermission, ChannelType, + Override, PermissionQuery, RelationshipStatus, DEFAULT_PERMISSION_DIRECT_MESSAGE, + DEFAULT_PERMISSION_SERVER, DEFAULT_PERMISSION_VIEW_ONLY, +}; + +#[async_std::test] +async fn validate_user_permissions() { + /// Scenario in which we are friends with a user + /// and we have a DM channel open with them + struct Scenario {} + let mut query = Scenario {}; + + let perms = calculate_user_permissions(&mut query).await; + assert_eq!(perms, u32::MAX); + + let perms = calculate_channel_permissions(&mut query).await; + let value: u64 = perms.into(); + assert_eq!(value, *DEFAULT_PERMISSION_DIRECT_MESSAGE); + + #[async_trait] + impl PermissionQuery for Scenario { + async fn are_we_privileged(&mut self) -> bool { + false + } + + async fn are_we_a_bot(&mut self) -> bool { + false + } + + async fn are_the_users_same(&mut self) -> bool { + false + } + + async fn user_relationship(&mut self) -> RelationshipStatus { + RelationshipStatus::Friend + } + + async fn user_is_bot(&mut self) -> bool { + false + } + + async fn have_mutual_connection(&mut self) -> bool { + false + } + + async fn are_we_server_owner(&mut self) -> bool { + unreachable!() + } + + async fn are_we_a_member(&mut self) -> bool { + unreachable!() + } + + async fn get_default_server_permissions(&mut self) -> u64 { + unreachable!() + } + + async fn get_our_server_role_overrides(&mut self) -> Vec { + unreachable!() + } + + async fn are_we_timed_out(&mut self) -> bool { + unreachable!() + } + + async fn get_channel_type(&mut self) -> ChannelType { + ChannelType::DirectMessage + } + + async fn get_default_channel_permissions(&mut self) -> Override { + unreachable!() + } + + async fn get_our_channel_role_overrides(&mut self) -> Vec { + unreachable!() + } + + async fn do_we_own_the_channel(&mut self) -> bool { + unreachable!() + } + + async fn are_we_part_of_the_channel(&mut self) -> bool { + true + } + + async fn set_recipient_as_user(&mut self) { + // no-op + } + + async fn set_server_from_channel(&mut self) { + unreachable!() + } + } +} + +#[async_std::test] +async fn validate_group_permissions() { + /// Scenario in which we are in a group channel with only talking permission + struct Scenario {} + let mut query = Scenario {}; + + let perms = calculate_channel_permissions(&mut query).await; + let value: u64 = perms.into(); + assert_eq!( + value, + *DEFAULT_PERMISSION_VIEW_ONLY | ChannelPermission::SendMessage as u64 + ); + + #[async_trait] + impl PermissionQuery for Scenario { + async fn are_we_privileged(&mut self) -> bool { + false + } + + async fn are_we_a_bot(&mut self) -> bool { + unreachable!() + } + + async fn are_the_users_same(&mut self) -> bool { + unreachable!() + } + + async fn user_relationship(&mut self) -> RelationshipStatus { + unreachable!() + } + + async fn user_is_bot(&mut self) -> bool { + unreachable!() + } + + async fn have_mutual_connection(&mut self) -> bool { + unreachable!() + } + + async fn are_we_server_owner(&mut self) -> bool { + unreachable!() + } + + async fn are_we_a_member(&mut self) -> bool { + unreachable!() + } + + async fn get_default_server_permissions(&mut self) -> u64 { + unreachable!() + } + + async fn get_our_server_role_overrides(&mut self) -> Vec { + unreachable!() + } + + async fn are_we_timed_out(&mut self) -> bool { + unreachable!() + } + + async fn get_channel_type(&mut self) -> ChannelType { + ChannelType::Group + } + + async fn get_default_channel_permissions(&mut self) -> Override { + Override { + allow: ChannelPermission::SendMessage as u64, + deny: 0, + } + } + + async fn get_our_channel_role_overrides(&mut self) -> Vec { + unreachable!() + } + + async fn do_we_own_the_channel(&mut self) -> bool { + false + } + + async fn are_we_part_of_the_channel(&mut self) -> bool { + true + } + + async fn set_recipient_as_user(&mut self) { + unreachable!() + } + + async fn set_server_from_channel(&mut self) { + unreachable!() + } + } +} + +#[async_std::test] +async fn validate_server_permissions() { + /// Scenario in which we are in a server channel where: + /// - the server grants reading history and sending messages by default + /// - we have a role that allows us to upload files and react but denies reading history + /// - however the channel disallows sending messages + /// - and removes our role specific react permission + struct Scenario {} + let mut query = Scenario {}; + + let perms = calculate_channel_permissions(&mut query).await; + let value: u64 = perms.into(); + assert_eq!( + value, + ChannelPermission::ViewChannel as u64 | ChannelPermission::UploadFiles as u64 + ); + + #[async_trait] + impl PermissionQuery for Scenario { + async fn are_we_privileged(&mut self) -> bool { + false + } + + async fn are_we_a_bot(&mut self) -> bool { + unreachable!() + } + + async fn are_the_users_same(&mut self) -> bool { + unreachable!() + } + + async fn user_relationship(&mut self) -> RelationshipStatus { + unreachable!() + } + + async fn user_is_bot(&mut self) -> bool { + unreachable!() + } + + async fn have_mutual_connection(&mut self) -> bool { + unreachable!() + } + + async fn are_we_server_owner(&mut self) -> bool { + false + } + + async fn are_we_a_member(&mut self) -> bool { + true + } + + async fn get_default_server_permissions(&mut self) -> u64 { + ChannelPermission::ViewChannel as u64 + | ChannelPermission::SendMessage as u64 + | ChannelPermission::ReadMessageHistory as u64 + } + + async fn get_our_server_role_overrides(&mut self) -> Vec { + vec![Override { + allow: ChannelPermission::UploadFiles as u64 | ChannelPermission::React as u64, + deny: ChannelPermission::ReadMessageHistory as u64, + }] + } + + async fn are_we_timed_out(&mut self) -> bool { + false + } + + async fn get_channel_type(&mut self) -> ChannelType { + ChannelType::ServerChannel + } + + async fn get_default_channel_permissions(&mut self) -> Override { + Override { + allow: 0, + deny: ChannelPermission::SendMessage as u64, + } + } + + async fn get_our_channel_role_overrides(&mut self) -> Vec { + vec![Override { + allow: 0, + deny: ChannelPermission::React as u64, + }] + } + + async fn do_we_own_the_channel(&mut self) -> bool { + unreachable!() + } + + async fn are_we_part_of_the_channel(&mut self) -> bool { + unreachable!() + } + + async fn set_recipient_as_user(&mut self) { + unreachable!() + } + + async fn set_server_from_channel(&mut self) { + // no-op + } + } +} + +#[async_std::test] +async fn validate_timed_out_member() { + /// Scenario in which we are in a server that we have been timed out from + struct Scenario {} + let mut query = Scenario {}; + + let perms = calculate_channel_permissions(&mut query).await; + let value: u64 = perms.into(); + assert_eq!(value, *DEFAULT_PERMISSION_VIEW_ONLY); + + #[async_trait] + impl PermissionQuery for Scenario { + async fn are_we_privileged(&mut self) -> bool { + false + } + + async fn are_we_a_bot(&mut self) -> bool { + unreachable!() + } + + async fn are_the_users_same(&mut self) -> bool { + unreachable!() + } + + async fn user_relationship(&mut self) -> RelationshipStatus { + unreachable!() + } + + async fn user_is_bot(&mut self) -> bool { + unreachable!() + } + + async fn have_mutual_connection(&mut self) -> bool { + unreachable!() + } + + async fn are_we_server_owner(&mut self) -> bool { + false + } + + async fn are_we_a_member(&mut self) -> bool { + true + } + + async fn get_default_server_permissions(&mut self) -> u64 { + *DEFAULT_PERMISSION_SERVER + } + + async fn get_our_server_role_overrides(&mut self) -> Vec { + vec![] + } + + async fn are_we_timed_out(&mut self) -> bool { + true + } + + async fn get_channel_type(&mut self) -> ChannelType { + ChannelType::ServerChannel + } + + async fn get_default_channel_permissions(&mut self) -> Override { + Override { allow: 0, deny: 0 } + } + + async fn get_our_channel_role_overrides(&mut self) -> Vec { + vec![] + } + + async fn do_we_own_the_channel(&mut self) -> bool { + unreachable!() + } + + async fn are_we_part_of_the_channel(&mut self) -> bool { + unreachable!() + } + + async fn set_recipient_as_user(&mut self) { + unreachable!() + } + + async fn set_server_from_channel(&mut self) { + // no-op + } + } +} diff --git a/crates/core/permissions/src/trait.rs b/crates/core/permissions/src/trait.rs new file mode 100644 index 00000000..d4a7c3d3 --- /dev/null +++ b/crates/core/permissions/src/trait.rs @@ -0,0 +1,67 @@ +use crate::{ChannelType, Override, RelationshipStatus}; + +#[async_trait] +pub trait PermissionQuery { + // * For calculating user permission + + /// Is our perspective user privileged? + async fn are_we_privileged(&mut self) -> bool; + + /// Is our perspective user a bot? + async fn are_we_a_bot(&mut self) -> bool; + + /// Is our perspective user and the currently selected user the same? + async fn are_the_users_same(&mut self) -> bool; + + /// Get the relationship with have with the currently selected user + async fn user_relationship(&mut self) -> RelationshipStatus; + + /// Whether the currently selected user is a bot + async fn user_is_bot(&mut self) -> bool; + + /// Do we have a mutual connection with the currently selected user? + async fn have_mutual_connection(&mut self) -> bool; + + // * For calculating server permission + + /// Is our perspective user the server's owner? + async fn are_we_server_owner(&mut self) -> bool; + + /// Is our perspective user a member of the server? + async fn are_we_a_member(&mut self) -> bool; + + /// Get default server permission + async fn get_default_server_permissions(&mut self) -> u64; + + /// Get the ordered role overrides (from lowest to highest) for this member in this server + async fn get_our_server_role_overrides(&mut self) -> Vec; + + /// Is our perspective user timed out on this server? + async fn are_we_timed_out(&mut self) -> bool; + + // * For calculating channel permission + + /// Get the type of the channel + async fn get_channel_type(&mut self) -> ChannelType; + + /// Get the default channel permissions + /// Group channel defaults should be mapped to an allow-only override + async fn get_default_channel_permissions(&mut self) -> Override; + + /// Get the ordered role overrides (from lowest to highest) for this member in this channel + async fn get_our_channel_role_overrides(&mut self) -> Vec; + + /// Do we own this group or saved messages channel if it is one of those? + async fn do_we_own_the_channel(&mut self) -> bool; + + /// Are we a recipient of this channel? + async fn are_we_part_of_the_channel(&mut self) -> bool; + + /// Set the current user as the recipient of this channel + /// (this will only ever be called for DirectMessage channels, use unimplemented!() for other code paths) + async fn set_recipient_as_user(&mut self); + + /// Set the current server as the server owning this channel + /// (this will only ever be called for server channels, use unimplemented!() for other code paths) + async fn set_server_from_channel(&mut self); +}