mirror of
https://github.com/stoatchat/stoatchat.git
synced 2026-07-14 05:26:59 +00:00
feat(core/permissions): initial commit
This commit is contained in:
33
crates/core/permissions/Cargo.toml
Normal file
33
crates/core/permissions/Cargo.toml
Normal file
@@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "revolt-permissions"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-or-later"
|
||||
authors = [ "Paul Makles <me@insrt.uk>" ]
|
||||
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 }
|
||||
128
crates/core/permissions/src/impl.rs
Normal file
128
crates/core/permissions/src/impl.rs
Normal file
@@ -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<P: PermissionQuery>(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<P: PermissionQuery>(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<P: PermissionQuery>(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(),
|
||||
}
|
||||
}
|
||||
16
crates/core/permissions/src/lib.rs
Normal file
16
crates/core/permissions/src/lib.rs
Normal file
@@ -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;
|
||||
137
crates/core/permissions/src/models/channel.rs
Normal file
137
crates/core/permissions/src/models/channel.rs
Normal file
@@ -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<u64> =
|
||||
Lazy::new(|| ChannelPermission::ViewChannel + ChannelPermission::ReadMessageHistory);
|
||||
|
||||
pub static DEFAULT_PERMISSION_VIEW_ONLY: Lazy<u64> =
|
||||
Lazy::new(|| ChannelPermission::ViewChannel + ChannelPermission::ReadMessageHistory);
|
||||
|
||||
pub static DEFAULT_PERMISSION: Lazy<u64> = 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<u64> = Lazy::new(|| {
|
||||
DEFAULT_PERMISSION.add(ChannelPermission::ManageChannel + ChannelPermission::React)
|
||||
});
|
||||
|
||||
pub static DEFAULT_PERMISSION_SERVER: Lazy<u64> = Lazy::new(|| {
|
||||
DEFAULT_PERMISSION.add(
|
||||
ChannelPermission::React
|
||||
+ ChannelPermission::ChangeNickname
|
||||
+ ChannelPermission::ChangeAvatar,
|
||||
)
|
||||
});
|
||||
58
crates/core/permissions/src/models/mod.rs
Normal file
58
crates/core/permissions/src/models/mod.rs
Normal file
@@ -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<i64> for PermissionValue {
|
||||
fn from(v: i64) -> Self {
|
||||
Self(v as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for PermissionValue {
|
||||
fn from(v: u64) -> Self {
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PermissionValue> for u64 {
|
||||
fn from(v: PermissionValue) -> Self {
|
||||
v.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChannelPermission> for PermissionValue {
|
||||
fn from(v: ChannelPermission) -> Self {
|
||||
(v as u64).into()
|
||||
}
|
||||
}
|
||||
56
crates/core/permissions/src/models/server.rs
Normal file
56
crates/core/permissions/src/models/server.rs
Normal file
@@ -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<Override> for OverrideField {
|
||||
fn from(v: Override) -> Self {
|
||||
Self {
|
||||
a: v.allow as i64,
|
||||
d: v.deny as i64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OverrideField> for Override {
|
||||
fn from(v: OverrideField) -> Self {
|
||||
Self {
|
||||
allow: v.a as u64,
|
||||
deny: v.d as u64,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*impl From<OverrideField> for Bson {
|
||||
fn from(v: OverrideField) -> Self {
|
||||
Self::Document(bson::to_document(&v).unwrap())
|
||||
}
|
||||
}*/
|
||||
25
crates/core/permissions/src/models/user.rs
Normal file
25
crates/core/permissions/src/models/user.rs
Normal file
@@ -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 });
|
||||
377
crates/core/permissions/src/test.rs
Normal file
377
crates/core/permissions/src/test.rs
Normal file
@@ -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<Override> {
|
||||
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<Override> {
|
||||
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<Override> {
|
||||
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<Override> {
|
||||
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<Override> {
|
||||
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<Override> {
|
||||
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<Override> {
|
||||
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<Override> {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
67
crates/core/permissions/src/trait.rs
Normal file
67
crates/core/permissions/src/trait.rs
Normal file
@@ -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<Override>;
|
||||
|
||||
/// 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<Override>;
|
||||
|
||||
/// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user