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