From 7919887ad2d698503744618a8cc1bc2149389810 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Tue, 19 Nov 2024 14:36:32 +0100 Subject: [PATCH 01/15] rewrite community permission to use strings --- Cargo.toml | 1 + extensions/warp-ipfs/src/lib.rs | 110 ++++++++++----- extensions/warp-ipfs/src/store/community.rs | 96 ++++++------- extensions/warp-ipfs/src/store/message.rs | 19 ++- .../src/store/message/community_task.rs | 59 ++++---- extensions/warp-ipfs/src/store/mod.rs | 21 ++- extensions/warp-ipfs/tests/community.rs | 4 +- tools/enum_macro/Cargo.toml | 12 ++ tools/enum_macro/src/lib.rs | 116 +++++++++++++++ warp/Cargo.toml | 1 + warp/src/raygun/community.rs | 132 ++++++++++++++---- warp/src/raygun/mod.rs | 21 ++- warp/src/warp.rs | 76 ++++++---- 13 files changed, 464 insertions(+), 204 deletions(-) create mode 100644 tools/enum_macro/Cargo.toml create mode 100644 tools/enum_macro/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index cb761177f..9f58acd7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ tracing = { version = "0.1" } either = "1" void = "1" indexmap = { version = "2.4.0", features = ["serde"] } +enum_macro = { path = "./tools/enum_macro" } # ipfs dependency rust-ipfs = "0.12.2" diff --git a/extensions/warp-ipfs/src/lib.rs b/extensions/warp-ipfs/src/lib.rs index ed68803e2..79312d66d 100644 --- a/extensions/warp-ipfs/src/lib.rs +++ b/extensions/warp-ipfs/src/lib.rs @@ -23,9 +23,7 @@ use std::time::Duration; use tokio_util::compat::TokioAsyncReadCompatExt; use tracing::{Instrument, Span}; use uuid::Uuid; -use warp::raygun::community::{ - CommunityChannelPermission, CommunityPermission, CommunityRole, RoleId, -}; +use warp::raygun::community::{CommunityRole, RoleId}; use crate::config::{Bootstrap, DiscoveryType}; use crate::rt::{Executor, LocalExecutor}; @@ -1879,42 +1877,54 @@ impl RayGunCommunity for WarpIpfs { .edit_community_description(community_id, description) .await } - async fn grant_community_permission( + async fn grant_community_permission( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .grant_community_permission(community_id, permission, role_id) + .grant_community_permission(community_id, permission.to_string(), role_id) .await } - async fn revoke_community_permission( + async fn revoke_community_permission( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .revoke_community_permission(community_id, permission, role_id) + .revoke_community_permission(community_id, permission.to_string(), role_id) .await } - async fn grant_community_permission_for_all( + async fn grant_community_permission_for_all( &mut self, community_id: Uuid, - permission: CommunityPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .grant_community_permission_for_all(community_id, permission) + .grant_community_permission_for_all(community_id, permission.to_string()) .await } - async fn revoke_community_permission_for_all( + async fn revoke_community_permission_for_all( &mut self, community_id: Uuid, - permission: CommunityPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .revoke_community_permission_for_all(community_id, permission) + .revoke_community_permission_for_all(community_id, permission.to_string()) .await } async fn remove_community_member( @@ -1947,46 +1957,76 @@ impl RayGunCommunity for WarpIpfs { .edit_community_channel_description(community_id, channel_id, description) .await } - async fn grant_community_channel_permission( + async fn grant_community_channel_permission( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .grant_community_channel_permission(community_id, channel_id, permission, role_id) + .grant_community_channel_permission( + community_id, + channel_id, + permission.to_string(), + role_id, + ) .await } - async fn revoke_community_channel_permission( + async fn revoke_community_channel_permission( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .revoke_community_channel_permission(community_id, channel_id, permission, role_id) + .revoke_community_channel_permission( + community_id, + channel_id, + permission.to_string(), + role_id, + ) .await } - async fn grant_community_channel_permission_for_all( + async fn grant_community_channel_permission_for_all( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .grant_community_channel_permission_for_all(community_id, channel_id, permission) + .grant_community_channel_permission_for_all( + community_id, + channel_id, + permission.to_string(), + ) .await } - async fn revoke_community_channel_permission_for_all( + async fn revoke_community_channel_permission_for_all( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.messaging_store()? - .revoke_community_channel_permission_for_all(community_id, channel_id, permission) + .revoke_community_channel_permission_for_all( + community_id, + channel_id, + permission.to_string(), + ) .await } diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index 6dc0f4a61..2aa967427 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -11,9 +11,8 @@ use warp::{ crypto::DID, error::Error, raygun::community::{ - Community, CommunityChannel, CommunityChannelPermission, CommunityChannelPermissions, - CommunityChannelType, CommunityInvite, CommunityPermission, CommunityPermissions, - CommunityRole, RoleId, + Community, CommunityChannel, CommunityChannelPermissions, CommunityChannelType, + CommunityInvite, CommunityPermission, CommunityPermissions, CommunityRole, RoleId, }, }; @@ -210,32 +209,9 @@ impl CommunityDocument { let creator = keypair.to_did()?; let mut permissions = CommunityPermissions::new(); - permissions.insert(CommunityPermission::EditName, IndexSet::new()); - permissions.insert(CommunityPermission::EditDescription, IndexSet::new()); - permissions.insert(CommunityPermission::EditIcon, IndexSet::new()); - permissions.insert(CommunityPermission::EditBanner, IndexSet::new()); - - permissions.insert(CommunityPermission::CreateRoles, IndexSet::new()); - permissions.insert(CommunityPermission::EditRoles, IndexSet::new()); - permissions.insert(CommunityPermission::DeleteRoles, IndexSet::new()); - - permissions.insert(CommunityPermission::GrantRoles, IndexSet::new()); - permissions.insert(CommunityPermission::RevokeRoles, IndexSet::new()); - - permissions.insert(CommunityPermission::GrantPermissions, IndexSet::new()); - permissions.insert(CommunityPermission::RevokePermissions, IndexSet::new()); - - permissions.insert(CommunityPermission::CreateChannels, IndexSet::new()); - permissions.insert(CommunityPermission::EditChannels, IndexSet::new()); - permissions.insert(CommunityPermission::DeleteChannels, IndexSet::new()); - - //We don't add CreateInvites permission since by default we leave it unrestricted. - permissions.insert(CommunityPermission::EditInvites, IndexSet::new()); - permissions.insert(CommunityPermission::DeleteInvites, IndexSet::new()); - - permissions.insert(CommunityPermission::RemoveMembers, IndexSet::new()); - - permissions.insert(CommunityPermission::DeleteMessages, IndexSet::new()); + for permission in CommunityPermission::default_disabled() { + permissions.insert(permission.to_string(), IndexSet::new()); + } let mut members = IndexSet::new(); members.insert(creator.clone()); @@ -319,32 +295,48 @@ impl CommunityDocument { } false } - pub fn has_permission(&self, user: &DID, has_permission: &CommunityPermission) -> bool { + pub fn has_permission(&self, user: &DID, has_permission: &T) -> bool + where + T: ToString, + { if &self.owner == user { return true; } if !self.members.contains(user) { return false; } - let Some(authorized_roles) = self.permissions.get(has_permission) else { - return true; - }; - for authorized_role in authorized_roles { - if let Some(role) = self.roles.get(&authorized_role.to_string()) { - if role.members.contains(user) { - return true; + let permission = has_permission.to_string(); + let nodes = permission.split('.'); + let mut prev = None; + for node in nodes.into_iter() { + let node = match prev { + Some(prev) => format!("{}.{}", prev, node), + None => node.to_string(), + }; + let Some(authorized_roles) = self.permissions.get(&node) else { + return true; + }; + for authorized_role in authorized_roles { + if let Some(role) = self.roles.get(&authorized_role.to_string()) { + if role.members.contains(user) { + return true; + } } } + prev = Some(node); } false } - pub fn has_channel_permission( + pub fn has_channel_permission( &self, user: &DID, - has_permission: &CommunityChannelPermission, + has_permission: &T, channel_id: Uuid, - ) -> bool { + ) -> bool + where + T: ToString, + { if &self.owner == user { return true; } @@ -354,15 +346,25 @@ impl CommunityDocument { let Some(channel) = self.channels.get(&channel_id.to_string()) else { return false; }; - let Some(authorized_roles) = channel.permissions.get(has_permission) else { - return true; - }; - for authorized_role in authorized_roles { - if let Some(role) = self.roles.get(&authorized_role.to_string()) { - if role.members.contains(user) { - return true; + let permission = has_permission.to_string(); + let nodes = permission.split('.'); + let mut prev = None; + for node in nodes.into_iter() { + let node = match prev { + Some(prev) => format!("{}.{}", prev, node), + None => node.to_string(), + }; + let Some(authorized_roles) = channel.permissions.get(&node) else { + return true; + }; + for authorized_role in authorized_roles { + if let Some(role) = self.roles.get(&authorized_role.to_string()) { + if role.members.contains(user) { + return true; + } } } + prev = Some(node); } false } diff --git a/extensions/warp-ipfs/src/store/message.rs b/extensions/warp-ipfs/src/store/message.rs index 7ecf8ef6e..969ed84dd 100644 --- a/extensions/warp-ipfs/src/store/message.rs +++ b/extensions/warp-ipfs/src/store/message.rs @@ -55,8 +55,7 @@ use crate::rt::{AbortableJoinHandle, Executor, LocalExecutor}; use crate::store::community::CommunityDocument; use chrono::{DateTime, Utc}; use warp::raygun::community::{ - Community, CommunityChannel, CommunityChannelPermission, CommunityChannelType, CommunityInvite, - CommunityPermission, CommunityRole, RoleId, + Community, CommunityChannel, CommunityChannelType, CommunityInvite, CommunityRole, RoleId, }; use warp::raygun::{ConversationImage, GroupPermissionOpt, Message}; use warp::{ @@ -1371,7 +1370,7 @@ impl MessageStore { pub async fn grant_community_permission( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let inner = &*self.inner.read().await; @@ -1394,7 +1393,7 @@ impl MessageStore { pub async fn revoke_community_permission( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let inner = &*self.inner.read().await; @@ -1417,7 +1416,7 @@ impl MessageStore { pub async fn grant_community_permission_for_all( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: String, ) -> Result<(), Error> { let inner = &*self.inner.read().await; let community_meta = inner @@ -1438,7 +1437,7 @@ impl MessageStore { pub async fn revoke_community_permission_for_all( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: String, ) -> Result<(), Error> { let inner = &*self.inner.read().await; let community_meta = inner @@ -1528,7 +1527,7 @@ impl MessageStore { &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let inner = &*self.inner.read().await; @@ -1553,7 +1552,7 @@ impl MessageStore { &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let inner = &*self.inner.read().await; @@ -1578,7 +1577,7 @@ impl MessageStore { &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, ) -> Result<(), Error> { let inner = &*self.inner.read().await; let community_meta = inner @@ -1603,7 +1602,7 @@ impl MessageStore { &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, ) -> Result<(), Error> { let inner = &*self.inner.read().await; let community_meta = inner diff --git a/extensions/warp-ipfs/src/store/message/community_task.rs b/extensions/warp-ipfs/src/store/message/community_task.rs index a7d91a5a1..a344a18ee 100644 --- a/extensions/warp-ipfs/src/store/message/community_task.rs +++ b/extensions/warp-ipfs/src/store/message/community_task.rs @@ -160,21 +160,21 @@ pub enum CommunityTaskCommand { response: oneshot::Sender>, }, GrantCommunityPermission { - permission: CommunityPermission, + permission: String, role_id: RoleId, response: oneshot::Sender>, }, RevokeCommunityPermission { - permission: CommunityPermission, + permission: String, role_id: RoleId, response: oneshot::Sender>, }, GrantCommunityPermissionForAll { - permission: CommunityPermission, + permission: String, response: oneshot::Sender>, }, RevokeCommunityPermissionForAll { - permission: CommunityPermission, + permission: String, response: oneshot::Sender>, }, RemoveCommunityMember { @@ -193,24 +193,24 @@ pub enum CommunityTaskCommand { }, GrantCommunityChannelPermission { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, response: oneshot::Sender>, }, RevokeCommunityChannelPermission { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, response: oneshot::Sender>, }, GrantCommunityChannelPermissionForAll { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, response: oneshot::Sender>, }, RevokeCommunityChannelPermissionForAll { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, response: oneshot::Sender>, }, GetCommunityChannelMessage { @@ -2007,7 +2007,7 @@ impl CommunityTask { pub async fn grant_community_permission( &mut self, - permission: CommunityPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); @@ -2025,7 +2025,7 @@ impl CommunityTask { None => { let mut roles = IndexSet::new(); roles.insert(role_id); - self.document.permissions.insert(permission, roles); + self.document.permissions.insert(permission.clone(), roles); } } self.set_document().await?; @@ -2034,7 +2034,7 @@ impl CommunityTask { .event_broadcast .send(MessageEventKind::GrantedCommunityPermission { community_id: self.community_id, - permission, + permission: permission.clone(), role_id, }); @@ -2054,7 +2054,7 @@ impl CommunityTask { } pub async fn revoke_community_permission( &mut self, - permission: CommunityPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); @@ -2069,12 +2069,11 @@ impl CommunityTask { authorized_roles.swap_remove(&role_id); } self.set_document().await?; - let _ = self .event_broadcast .send(MessageEventKind::RevokedCommunityPermission { community_id: self.community_id, - permission, + permission: permission.clone(), role_id, }); @@ -2094,7 +2093,7 @@ impl CommunityTask { } pub async fn grant_community_permission_for_all( &mut self, - permission: CommunityPermission, + permission: String, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); if !self @@ -2115,7 +2114,7 @@ impl CommunityTask { .event_broadcast .send(MessageEventKind::GrantedCommunityPermissionForAll { community_id: self.community_id, - permission, + permission: permission.clone(), }); self.publish( @@ -2131,7 +2130,7 @@ impl CommunityTask { } pub async fn revoke_community_permission_for_all( &mut self, - permission: CommunityPermission, + permission: String, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); if !self @@ -2143,14 +2142,14 @@ impl CommunityTask { self.document .permissions - .insert(permission, IndexSet::new()); + .insert(permission.clone(), IndexSet::new()); self.set_document().await?; let _ = self .event_broadcast .send(MessageEventKind::RevokedCommunityPermissionForAll { community_id: self.community_id, - permission, + permission: permission.clone(), }); self.publish( @@ -2287,7 +2286,7 @@ impl CommunityTask { pub async fn grant_community_channel_permission( &mut self, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); @@ -2310,7 +2309,7 @@ impl CommunityTask { None => { let mut roles = IndexSet::new(); roles.insert(role_id); - channel_doc.permissions.insert(permission, roles); + channel_doc.permissions.insert(permission.clone(), roles); } } self.set_document().await?; @@ -2320,7 +2319,7 @@ impl CommunityTask { .send(MessageEventKind::GrantedCommunityChannelPermission { community_id: self.community_id, channel_id, - permission, + permission: permission.clone(), role_id, }); @@ -2342,7 +2341,7 @@ impl CommunityTask { pub async fn revoke_community_channel_permission( &mut self, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); @@ -2368,7 +2367,7 @@ impl CommunityTask { .send(MessageEventKind::RevokedCommunityChannelPermission { community_id: self.community_id, channel_id, - permission, + permission: permission.clone(), role_id, }); @@ -2390,7 +2389,7 @@ impl CommunityTask { pub async fn grant_community_channel_permission_for_all( &mut self, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); if !self @@ -2417,7 +2416,7 @@ impl CommunityTask { .send(MessageEventKind::GrantedCommunityChannelPermissionForAll { community_id: self.community_id, channel_id, - permission, + permission: permission.clone(), }); self.publish( @@ -2437,7 +2436,7 @@ impl CommunityTask { pub async fn revoke_community_channel_permission_for_all( &mut self, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, ) -> Result<(), Error> { let own_did = &self.identity.did_key(); if !self @@ -2452,7 +2451,9 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - channel_doc.permissions.insert(permission, IndexSet::new()); + channel_doc + .permissions + .insert(permission.clone(), IndexSet::new()); self.set_document().await?; let _ = @@ -2460,7 +2461,7 @@ impl CommunityTask { .send(MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: self.community_id, channel_id, - permission, + permission: permission.clone(), }); self.publish( diff --git a/extensions/warp-ipfs/src/store/mod.rs b/extensions/warp-ipfs/src/store/mod.rs index a9ae98533..d23538da0 100644 --- a/extensions/warp-ipfs/src/store/mod.rs +++ b/extensions/warp-ipfs/src/store/mod.rs @@ -30,10 +30,7 @@ use warp::{ }, error::Error, multipass::identity::IdentityStatus, - raygun::{ - community::{CommunityChannelPermission, CommunityPermission, RoleId}, - GroupPermissions, MessageEvent, PinState, ReactionState, - }, + raygun::{community::RoleId, GroupPermissions, MessageEvent, PinState, ReactionState}, }; use conversation::{message::MessageDocument, ConversationDocument}; @@ -504,18 +501,18 @@ pub enum CommunityUpdateKind { description: Option, }, GrantCommunityPermission { - permission: CommunityPermission, + permission: String, role_id: RoleId, }, RevokeCommunityPermission { - permission: CommunityPermission, + permission: String, role_id: RoleId, }, GrantCommunityPermissionForAll { - permission: CommunityPermission, + permission: String, }, RevokeCommunityPermissionForAll { - permission: CommunityPermission, + permission: String, }, RemoveCommunityMember { member: DID, @@ -530,21 +527,21 @@ pub enum CommunityUpdateKind { }, GrantCommunityChannelPermission { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, }, RevokeCommunityChannelPermission { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, }, GrantCommunityChannelPermissionForAll { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, }, RevokeCommunityChannelPermissionForAll { channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, }, } diff --git a/extensions/warp-ipfs/tests/community.rs b/extensions/warp-ipfs/tests/community.rs index fe3c25633..63fd35ab9 100644 --- a/extensions/warp-ipfs/tests/community.rs +++ b/extensions/warp-ipfs/tests/community.rs @@ -1558,7 +1558,7 @@ mod test { let community = instance_a.get_community(community.id()).await?; assert!(community .permissions() - .get(&CommunityPermission::EditName) + .get(&CommunityPermission::EditName.to_string()) .unwrap() .contains(&role.id())); Ok(()) @@ -2107,7 +2107,7 @@ mod test { .await?; assert!(channel .permissions() - .get(&CommunityChannelPermission::ViewChannel) + .get(&CommunityChannelPermission::ViewChannel.to_string()) .is_none()); Ok(()) } diff --git a/tools/enum_macro/Cargo.toml b/tools/enum_macro/Cargo.toml new file mode 100644 index 000000000..b40b50536 --- /dev/null +++ b/tools/enum_macro/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "enum_macro" +version = "0.1.0" +edition = "2021" + +[dependencies] +syn = { version = "2.0.87", features = ["derive"] } +quote = "1.0.37" +proc-macro2 = "1.0.89" + +[lib] +proc-macro = true \ No newline at end of file diff --git a/tools/enum_macro/src/lib.rs b/tools/enum_macro/src/lib.rs new file mode 100644 index 000000000..2f22c9adf --- /dev/null +++ b/tools/enum_macro/src/lib.rs @@ -0,0 +1,116 @@ +extern crate proc_macro2; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DeriveInput}; + +/// +/// Implements Display for enum values with formatting and snake_case conversion +/// This is used for CommunityPermissions +/// E.g. +/// ```rust +/// #[derive(EnumFormatting)] +/// pub enum SomeEnum { +/// #[permission(formatting="prefix.{}")] +/// SomeValue1, +/// SomeValue2 +/// } +/// +/// assert_eq!(SomeEnum::SomeValue1.to_string(), "prefix.some_value_1") +/// assert_eq!(SomeEnum::SomeValue2.to_string(), "some_value_2") +/// ``` +#[proc_macro_derive(EnumFormatting, attributes(permission))] +pub fn permission_node(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = &input.ident; + if let Data::Enum(enum_data) = &input.data { + let mut variants = vec![]; + for variant in &enum_data.variants { + let mut format = None; + if let Some(attr) = variant + .attrs + .iter() + .find(|attr| attr.path().is_ident("permission")) + { + if let syn::Meta::List(l) = &attr.meta { + if let Err(err) = l.parse_nested_meta(|meta| { + if meta.path.is_ident("formatting") { + let exp: syn::Expr = meta.value()?.parse()?; + if let syn::Expr::Lit(v) = exp { + if let syn::Lit::Str(s) = v.lit { + format = Some(s.value()); + } + } + return Ok(()); + } + Err(meta.error("Unrecognized attribute")) + }) { + panic!("{:?}", err); + } + } + } + let format = format.unwrap_or("{}".into()); + + let variant_name = &variant.ident; + + variants.push(quote! { + #name::#variant_name => { + let mut snake = String::new(); + for (i, ch) in stringify!(#variant_name).char_indices() { + if i > 0 && ch.is_uppercase() { + snake.push('_'); + } + snake.push(ch.to_ascii_lowercase()); + } + format!(#format, snake) + }, + }); + } + + let expanded = quote! { + impl std::fmt::Display for #name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let result = match self { + #(#variants)* + }; + f.write_str(&format!("{}", result)) + } + } + }; + + TokenStream::from(expanded) + } else { + panic!("Only Enums are supported"); + } +} + +/// +/// Implements #values() for enum values that returns all defined values in the enum +/// E.g. +/// ```rust +/// #[derive(EnumValues, PartialEq)] +/// pub enum SomeEnum { +/// SomeValue1, +/// SomeValue2 +/// } +/// +/// assert_eq!([SomeEnum::SomeValue1, SomeEnum::SomeValue2], SomeEnum::values()) +/// ``` +#[proc_macro_derive(EnumValues)] +pub fn enum_values(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let name = &input.ident; + if let Data::Enum(enum_data) = input.data { + let variants: Vec<_> = enum_data.variants.into_iter().map(|v| v.ident).collect(); + let expanded = quote! { + impl #name { + pub fn values() -> &'static[#name] { + &[ #(#name::#variants),* ] + } + } + }; + TokenStream::from(expanded) + } else { + panic!("Only Enums are supported"); + } +} diff --git a/warp/Cargo.toml b/warp/Cargo.toml index 29d2f3b1d..296c5343d 100644 --- a/warp/Cargo.toml +++ b/warp/Cargo.toml @@ -60,6 +60,7 @@ tracing = { workspace = true } mediatype.workspace = true send_wrapper.workspace = true indexmap.workspace = true +enum_macro.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tokio = { workspace = true } diff --git a/warp/src/raygun/community.rs b/warp/src/raygun/community.rs index 5145f55bb..941e144dd 100644 --- a/warp/src/raygun/community.rs +++ b/warp/src/raygun/community.rs @@ -15,11 +15,12 @@ use super::{ AttachmentEventStream, ConversationImage, Message, MessageEvent, MessageEventStream, MessageOptions, MessageReference, MessageStatus, Messages, PinState, ReactionState, }; +use enum_macro::{EnumFormatting, EnumValues}; pub type RoleId = Uuid; pub type CommunityRoles = IndexMap; -pub type CommunityPermissions = IndexMap>; -pub type CommunityChannelPermissions = IndexMap>; +pub type CommunityPermissions = IndexMap>; +pub type CommunityChannelPermissions = IndexMap>; #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct CommunityRole { @@ -237,41 +238,88 @@ pub enum CommunityChannelType { VoiceEnabled, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case")] +/// Built-in CommunityPermission for ease of access +#[derive(Debug, Copy, Clone, EnumFormatting, EnumValues, PartialEq, Eq, Hash)] pub enum CommunityPermission { + #[permission(formatting = "community.visuals.{}")] EditName, + #[permission(formatting = "community.visuals.{}")] EditDescription, + #[permission(formatting = "community.visuals.{}")] EditIcon, + #[permission(formatting = "community.visuals.{}")] EditBanner, + #[permission(formatting = "community.roles.{}")] CreateRoles, + #[permission(formatting = "community.roles.{}")] EditRoles, + #[permission(formatting = "community.roles.{}")] DeleteRoles, + #[permission(formatting = "community.roles.{}")] GrantRoles, + #[permission(formatting = "community.roles.{}")] RevokeRoles, + #[permission(formatting = "community.permissions.{}")] GrantPermissions, + #[permission(formatting = "community.permissions.{}")] RevokePermissions, + #[permission(formatting = "community.invites.{}")] CreateInvites, + #[permission(formatting = "community.invites.{}")] EditInvites, + #[permission(formatting = "community.invites.{}")] DeleteInvites, + #[permission(formatting = "community.channels.{}")] CreateChannels, + #[permission(formatting = "community.channels.{}")] EditChannels, + #[permission(formatting = "community.channels.{}")] DeleteChannels, + #[permission(formatting = "community.members.{}")] RemoveMembers, + #[permission(formatting = "community.messages.{}")] DeleteMessages, } -#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case")] +impl CommunityPermission { + pub fn default_disabled() -> Vec { + vec![ + CommunityPermission::EditName, + CommunityPermission::EditDescription, + CommunityPermission::EditIcon, + CommunityPermission::EditBanner, + CommunityPermission::CreateRoles, + CommunityPermission::EditRoles, + CommunityPermission::DeleteRoles, + CommunityPermission::GrantRoles, + CommunityPermission::RevokeRoles, + CommunityPermission::GrantPermissions, + CommunityPermission::RevokePermissions, + //We don't add CreateInvites permission since by default we leave it unrestricted. + CommunityPermission::EditInvites, + CommunityPermission::DeleteInvites, + CommunityPermission::CreateChannels, + CommunityPermission::EditChannels, + CommunityPermission::DeleteChannels, + CommunityPermission::RemoveMembers, + CommunityPermission::DeleteMessages, + ] + } +} + +/// Built-in CommunityChannelPermission for ease of access +#[derive(Debug, Copy, Clone, EnumFormatting, EnumValues, PartialEq, Eq, Hash)] pub enum CommunityChannelPermission { + #[permission(formatting = "community_channel.{}")] ViewChannel, + #[permission(formatting = "community_channel.{}")] SendMessages, } @@ -442,34 +490,46 @@ pub trait RayGunCommunity: Sync + Send { ) -> Result<(), Error> { Err(Error::Unimplemented) } - async fn grant_community_permission( + async fn grant_community_permission( &mut self, _community_id: Uuid, - _permission: CommunityPermission, + _permission: T, _role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } - async fn revoke_community_permission( + async fn revoke_community_permission( &mut self, _community_id: Uuid, - _permission: CommunityPermission, + _permission: T, _role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } - async fn grant_community_permission_for_all( + async fn grant_community_permission_for_all( &mut self, _community_id: Uuid, - _permission: CommunityPermission, - ) -> Result<(), Error> { + _permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } - async fn revoke_community_permission_for_all( + async fn revoke_community_permission_for_all( &mut self, _community_id: Uuid, - _permission: CommunityPermission, - ) -> Result<(), Error> { + _permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } async fn remove_community_member( @@ -496,38 +556,50 @@ pub trait RayGunCommunity: Sync + Send { ) -> Result<(), Error> { Err(Error::Unimplemented) } - async fn grant_community_channel_permission( + async fn grant_community_channel_permission( &mut self, _community_id: Uuid, _channel_id: Uuid, - _permission: CommunityChannelPermission, + _permission: T, _role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } - async fn revoke_community_channel_permission( + async fn revoke_community_channel_permission( &mut self, _community_id: Uuid, _channel_id: Uuid, - _permission: CommunityChannelPermission, + _permission: T, _role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } - async fn grant_community_channel_permission_for_all( + async fn grant_community_channel_permission_for_all( &mut self, _community_id: Uuid, _channel_id: Uuid, - _permission: CommunityChannelPermission, - ) -> Result<(), Error> { + _permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } - async fn revoke_community_channel_permission_for_all( + async fn revoke_community_channel_permission_for_all( &mut self, _community_id: Uuid, _channel_id: Uuid, - _permission: CommunityChannelPermission, - ) -> Result<(), Error> { + _permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { Err(Error::Unimplemented) } diff --git a/warp/src/raygun/mod.rs b/warp/src/raygun/mod.rs index 9fc03c120..fef994dfb 100644 --- a/warp/src/raygun/mod.rs +++ b/warp/src/raygun/mod.rs @@ -8,10 +8,7 @@ use crate::error::Error; use crate::raygun::community::RayGunCommunity; use crate::{Extension, SingleHandle}; -use community::{ - CommunityChannel, CommunityChannelPermission, CommunityInvite, CommunityPermission, - CommunityRole, RoleId, -}; +use community::{CommunityChannel, CommunityInvite, CommunityRole, RoleId}; use derive_more::Display; use futures::stream::BoxStream; @@ -190,21 +187,21 @@ pub enum MessageEventKind { }, GrantedCommunityPermission { community_id: Uuid, - permission: CommunityPermission, + permission: String, role_id: RoleId, }, RevokedCommunityPermission { community_id: Uuid, - permission: CommunityPermission, + permission: String, role_id: RoleId, }, GrantedCommunityPermissionForAll { community_id: Uuid, - permission: CommunityPermission, + permission: String, }, RevokedCommunityPermissionForAll { community_id: Uuid, - permission: CommunityPermission, + permission: String, }, RemovedCommunityMember { community_id: Uuid, @@ -223,24 +220,24 @@ pub enum MessageEventKind { GrantedCommunityChannelPermission { community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, }, RevokedCommunityChannelPermission { community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, role_id: RoleId, }, GrantedCommunityChannelPermissionForAll { community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, }, RevokedCommunityChannelPermissionForAll { community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: String, }, } diff --git a/warp/src/warp.rs b/warp/src/warp.rs index a8d13f9e4..a6ae13ecf 100644 --- a/warp/src/warp.rs +++ b/warp/src/warp.rs @@ -15,9 +15,7 @@ use crate::multipass::{ Friends, GetIdentity, IdentityImportOption, IdentityInformation, ImportLocation, LocalIdentity, MultiPass, MultiPassEvent, MultiPassEventStream, MultiPassImportExport, }; -use crate::raygun::community::{ - CommunityChannelPermission, CommunityPermission, CommunityRole, RoleId, -}; +use crate::raygun::community::{CommunityRole, RoleId}; use crate::raygun::{ community::{ Community, CommunityChannel, CommunityChannelType, CommunityInvite, RayGunCommunity, @@ -765,40 +763,52 @@ where .edit_community_description(community_id, description) .await } - async fn grant_community_permission( + async fn grant_community_permission( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .grant_community_permission(community_id, permission, role_id) .await } - async fn revoke_community_permission( + async fn revoke_community_permission( &mut self, community_id: Uuid, - permission: CommunityPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .revoke_community_permission(community_id, permission, role_id) .await } - async fn grant_community_permission_for_all( + async fn grant_community_permission_for_all( &mut self, community_id: Uuid, - permission: CommunityPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .grant_community_permission_for_all(community_id, permission) .await } - async fn revoke_community_permission_for_all( + async fn revoke_community_permission_for_all( &mut self, community_id: Uuid, - permission: CommunityPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .revoke_community_permission_for_all(community_id, permission) .await @@ -833,44 +843,56 @@ where .edit_community_channel_description(community_id, channel_id, description) .await } - async fn grant_community_channel_permission( + async fn grant_community_channel_permission( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .grant_community_channel_permission(community_id, channel_id, permission, role_id) .await } - async fn revoke_community_channel_permission( + async fn revoke_community_channel_permission( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, + permission: T, role_id: RoleId, - ) -> Result<(), Error> { + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .revoke_community_channel_permission(community_id, channel_id, permission, role_id) .await } - async fn grant_community_channel_permission_for_all( + async fn grant_community_channel_permission_for_all( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .grant_community_channel_permission_for_all(community_id, channel_id, permission) .await } - async fn revoke_community_channel_permission_for_all( + async fn revoke_community_channel_permission_for_all( &mut self, community_id: Uuid, channel_id: Uuid, - permission: CommunityChannelPermission, - ) -> Result<(), Error> { + permission: T, + ) -> Result<(), Error> + where + T: ToString + Send, + { self.raygun .revoke_community_channel_permission_for_all(community_id, channel_id, permission) .await From 10e7a218ab0a95a8d0ac7df841cfd862ea5a803b Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Tue, 19 Nov 2024 16:53:37 +0100 Subject: [PATCH 02/15] test and permission check fix --- extensions/warp-ipfs/src/store/community.rs | 62 ++++++++++----------- tools/enum_macro/src/lib.rs | 16 +++--- 2 files changed, 36 insertions(+), 42 deletions(-) diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index 2aa967427..b880e7c1a 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -305,25 +305,18 @@ impl CommunityDocument { if !self.members.contains(user) { return false; } - let permission = has_permission.to_string(); - let nodes = permission.split('.'); - let mut prev = None; - for node in nodes.into_iter() { - let node = match prev { - Some(prev) => format!("{}.{}", prev, node), - None => node.to_string(), - }; - let Some(authorized_roles) = self.permissions.get(&node) else { - return true; - }; - for authorized_role in authorized_roles { - if let Some(role) = self.roles.get(&authorized_role.to_string()) { - if role.members.contains(user) { - return true; - } + // TODO: What happens with conflicting roles. ATM it finds the first applicable + let permissions = parse_permission(&has_permission.to_string()); + let authorized_roles = permissions.iter().find_map(|node|self.permissions.get(node)); + let Some(authorized_roles) = authorized_roles else { + return true; + }; + for authorized_role in authorized_roles { + if let Some(role) = self.roles.get(&authorized_role.to_string()) { + if role.members.contains(user) { + return true; } } - prev = Some(node); } false } @@ -346,29 +339,30 @@ impl CommunityDocument { let Some(channel) = self.channels.get(&channel_id.to_string()) else { return false; }; - let permission = has_permission.to_string(); - let nodes = permission.split('.'); - let mut prev = None; - for node in nodes.into_iter() { - let node = match prev { - Some(prev) => format!("{}.{}", prev, node), - None => node.to_string(), - }; - let Some(authorized_roles) = channel.permissions.get(&node) else { - return true; - }; - for authorized_role in authorized_roles { - if let Some(role) = self.roles.get(&authorized_role.to_string()) { - if role.members.contains(user) { - return true; - } + // TODO: What happens with conflicting roles. ATM it finds the first applicable + let permissions = parse_permission(&has_permission.to_string()); + let authorized_roles = permissions.iter().find_map(|node|channel.permissions.get(node)); + let Some(authorized_roles) = authorized_roles else { + return true; + }; + for authorized_role in authorized_roles { + if let Some(role) = self.roles.get(&authorized_role.to_string()) { + if role.members.contains(user) { + return true; } } - prev = Some(node); } false } } +fn parse_permission(permission: &String) -> Vec { + let split: Vec<_> = permission.split(".").collect(); + let mut nodes = vec![]; + for i in (1..=split.len()).rev() { + nodes.push(split[0..i].join(".")); + } + nodes +} #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct CommunityChannelDocument { pub id: Uuid, diff --git a/tools/enum_macro/src/lib.rs b/tools/enum_macro/src/lib.rs index 2f22c9adf..930162f2d 100644 --- a/tools/enum_macro/src/lib.rs +++ b/tools/enum_macro/src/lib.rs @@ -4,11 +4,11 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, Data, DeriveInput}; -/// /// Implements Display for enum values with formatting and snake_case conversion /// This is used for CommunityPermissions /// E.g. -/// ```rust +/// ``` +/// use enum_macro::EnumFormatting; /// #[derive(EnumFormatting)] /// pub enum SomeEnum { /// #[permission(formatting="prefix.{}")] @@ -16,8 +16,8 @@ use syn::{parse_macro_input, Data, DeriveInput}; /// SomeValue2 /// } /// -/// assert_eq!(SomeEnum::SomeValue1.to_string(), "prefix.some_value_1") -/// assert_eq!(SomeEnum::SomeValue2.to_string(), "some_value_2") +/// assert_eq!(SomeEnum::SomeValue1.to_string(), "prefix.some_value1"); +/// assert_eq!(SomeEnum::SomeValue2.to_string(), "some_value2"); /// ``` #[proc_macro_derive(EnumFormatting, attributes(permission))] pub fn permission_node(input: TokenStream) -> TokenStream { @@ -84,17 +84,17 @@ pub fn permission_node(input: TokenStream) -> TokenStream { } } -/// /// Implements #values() for enum values that returns all defined values in the enum /// E.g. -/// ```rust -/// #[derive(EnumValues, PartialEq)] +/// ``` +/// use enum_macro::EnumValues; +/// #[derive(EnumValues, PartialEq, Debug)] /// pub enum SomeEnum { /// SomeValue1, /// SomeValue2 /// } /// -/// assert_eq!([SomeEnum::SomeValue1, SomeEnum::SomeValue2], SomeEnum::values()) +/// assert_eq!([SomeEnum::SomeValue1, SomeEnum::SomeValue2], SomeEnum::values()); /// ``` #[proc_macro_derive(EnumValues)] pub fn enum_values(input: TokenStream) -> TokenStream { From 1a5a89905e83c9bf33810404b8d109fa280a3146 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Tue, 19 Nov 2024 16:59:35 +0100 Subject: [PATCH 03/15] linter --- extensions/warp-ipfs/src/store/community.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index b880e7c1a..51025f9ff 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -307,7 +307,9 @@ impl CommunityDocument { } // TODO: What happens with conflicting roles. ATM it finds the first applicable let permissions = parse_permission(&has_permission.to_string()); - let authorized_roles = permissions.iter().find_map(|node|self.permissions.get(node)); + let authorized_roles = permissions + .iter() + .find_map(|node| self.permissions.get(node)); let Some(authorized_roles) = authorized_roles else { return true; }; @@ -341,7 +343,9 @@ impl CommunityDocument { }; // TODO: What happens with conflicting roles. ATM it finds the first applicable let permissions = parse_permission(&has_permission.to_string()); - let authorized_roles = permissions.iter().find_map(|node|channel.permissions.get(node)); + let authorized_roles = permissions + .iter() + .find_map(|node| channel.permissions.get(node)); let Some(authorized_roles) = authorized_roles else { return true; }; From 3bbc862d7c46d95672d67bce8aa0199bed1a727e Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Tue, 19 Nov 2024 19:03:27 +0100 Subject: [PATCH 04/15] linter --- extensions/warp-ipfs/src/store/community.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index 51025f9ff..4fb286d39 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -359,7 +359,7 @@ impl CommunityDocument { false } } -fn parse_permission(permission: &String) -> Vec { +fn parse_permission(permission: &str) -> Vec { let split: Vec<_> = permission.split(".").collect(); let mut nodes = vec![]; for i in (1..=split.len()).rev() { From 3937d3e5c95341e389766a931dc7c6a536ba93a6 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Tue, 19 Nov 2024 19:26:33 +0100 Subject: [PATCH 05/15] comment --- extensions/warp-ipfs/src/store/community.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index 4fb286d39..f21d54b85 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -305,7 +305,6 @@ impl CommunityDocument { if !self.members.contains(user) { return false; } - // TODO: What happens with conflicting roles. ATM it finds the first applicable let permissions = parse_permission(&has_permission.to_string()); let authorized_roles = permissions .iter() @@ -341,7 +340,6 @@ impl CommunityDocument { let Some(channel) = self.channels.get(&channel_id.to_string()) else { return false; }; - // TODO: What happens with conflicting roles. ATM it finds the first applicable let permissions = parse_permission(&has_permission.to_string()); let authorized_roles = permissions .iter() @@ -359,6 +357,8 @@ impl CommunityDocument { false } } +/// Turns a permission string into nodes to check against +/// E.g. some.random.permission -> vec!["some.random.permission", "some.random", "some"] fn parse_permission(permission: &str) -> Vec { let split: Vec<_> = permission.split(".").collect(); let mut nodes = vec![]; From fabd8df2ee46cde72660646b1926ed669117795f Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Fri, 22 Nov 2024 16:41:48 +0100 Subject: [PATCH 06/15] update sub permission handling --- extensions/warp-ipfs/src/store/community.rs | 23 +- .../src/store/message/community_task.rs | 209 ++++++++++++------ extensions/warp-ipfs/src/store/mod.rs | 16 +- warp/src/error.rs | 2 + warp/src/raygun/community.rs | 18 ++ warp/src/raygun/mod.rs | 16 +- 6 files changed, 178 insertions(+), 106 deletions(-) diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index f21d54b85..f8d98b1df 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -305,11 +305,7 @@ impl CommunityDocument { if !self.members.contains(user) { return false; } - let permissions = parse_permission(&has_permission.to_string()); - let authorized_roles = permissions - .iter() - .find_map(|node| self.permissions.get(node)); - let Some(authorized_roles) = authorized_roles else { + let Some(authorized_roles) = self.permissions.get(&has_permission.to_string()) else { return true; }; for authorized_role in authorized_roles { @@ -321,7 +317,6 @@ impl CommunityDocument { } false } - pub fn has_channel_permission( &self, user: &DID, @@ -340,11 +335,7 @@ impl CommunityDocument { let Some(channel) = self.channels.get(&channel_id.to_string()) else { return false; }; - let permissions = parse_permission(&has_permission.to_string()); - let authorized_roles = permissions - .iter() - .find_map(|node| channel.permissions.get(node)); - let Some(authorized_roles) = authorized_roles else { + let Some(authorized_roles) = channel.permissions.get(&has_permission.to_string()) else { return true; }; for authorized_role in authorized_roles { @@ -357,16 +348,6 @@ impl CommunityDocument { false } } -/// Turns a permission string into nodes to check against -/// E.g. some.random.permission -> vec!["some.random.permission", "some.random", "some"] -fn parse_permission(permission: &str) -> Vec { - let split: Vec<_> = permission.split(".").collect(); - let mut nodes = vec![]; - for i in (1..=split.len()).rev() { - nodes.push(split[0..i].join(".")); - } - nodes -} #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct CommunityChannelDocument { pub id: Uuid, diff --git a/extensions/warp-ipfs/src/store/message/community_task.rs b/extensions/warp-ipfs/src/store/message/community_task.rs index a344a18ee..2efa9c103 100644 --- a/extensions/warp-ipfs/src/store/message/community_task.rs +++ b/extensions/warp-ipfs/src/store/message/community_task.rs @@ -2018,14 +2018,23 @@ impl CommunityTask { return Err(Error::Unauthorized); } - match self.document.permissions.get_mut(&permission) { - Some(authorized_roles) => { - authorized_roles.insert(role_id); - } - None => { - let mut roles = IndexSet::new(); - roles.insert(role_id); - self.document.permissions.insert(permission.clone(), roles); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); + } + for permission in &permissions { + match self.document.permissions.get_mut(permission) { + Some(authorized_roles) => { + authorized_roles.insert(role_id); + } + None => { + let mut roles = IndexSet::new(); + roles.insert(role_id); + self.document.permissions.insert(permission.clone(), roles); + } } } self.set_document().await?; @@ -2034,7 +2043,7 @@ impl CommunityTask { .event_broadcast .send(MessageEventKind::GrantedCommunityPermission { community_id: self.community_id, - permission: permission.clone(), + permissions: permissions.clone(), role_id, }); @@ -2043,7 +2052,7 @@ impl CommunityTask { CommunityMessagingEvents::UpdateCommunity { community: self.document.clone(), kind: CommunityUpdateKind::GrantCommunityPermission { - permission, + permissions, role_id, }, }, @@ -2065,15 +2074,24 @@ impl CommunityTask { return Err(Error::Unauthorized); } - if let Some(authorized_roles) = self.document.permissions.get_mut(&permission) { - authorized_roles.swap_remove(&role_id); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); + } + for permission in &permissions { + if let Some(authorized_roles) = self.document.permissions.get_mut(permission) { + authorized_roles.swap_remove(&role_id); + } } self.set_document().await?; let _ = self .event_broadcast .send(MessageEventKind::RevokedCommunityPermission { community_id: self.community_id, - permission: permission.clone(), + permissions: permissions.clone(), role_id, }); @@ -2082,7 +2100,7 @@ impl CommunityTask { CommunityMessagingEvents::UpdateCommunity { community: self.document.clone(), kind: CommunityUpdateKind::RevokeCommunityPermission { - permission, + permissions, role_id, }, }, @@ -2103,25 +2121,34 @@ impl CommunityTask { return Err(Error::Unauthorized); } - if self.document.permissions.contains_key(&permission) { - self.document.permissions.swap_remove(&permission); - self.set_document().await?; - } else { - return Err(Error::PermissionAlreadyGranted); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); + } + for permission in &permissions { + if self.document.permissions.contains_key(permission) { + self.document.permissions.swap_remove(permission); + self.set_document().await?; + } else if permissions.len() == 1 { + return Err(Error::PermissionAlreadyGranted); + } } let _ = self .event_broadcast .send(MessageEventKind::GrantedCommunityPermissionForAll { community_id: self.community_id, - permission: permission.clone(), + permissions: permissions.clone(), }); self.publish( None, CommunityMessagingEvents::UpdateCommunity { community: self.document.clone(), - kind: CommunityUpdateKind::GrantCommunityPermissionForAll { permission }, + kind: CommunityUpdateKind::GrantCommunityPermissionForAll { permissions }, }, true, vec![], @@ -2140,23 +2167,32 @@ impl CommunityTask { return Err(Error::Unauthorized); } - self.document - .permissions - .insert(permission.clone(), IndexSet::new()); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); + } + for permission in &permissions { + self.document + .permissions + .insert(permission.clone(), IndexSet::new()); + } self.set_document().await?; let _ = self .event_broadcast .send(MessageEventKind::RevokedCommunityPermissionForAll { community_id: self.community_id, - permission: permission.clone(), + permissions: permissions.clone(), }); self.publish( None, CommunityMessagingEvents::UpdateCommunity { community: self.document.clone(), - kind: CommunityUpdateKind::RevokeCommunityPermissionForAll { permission }, + kind: CommunityUpdateKind::RevokeCommunityPermissionForAll { permissions }, }, true, vec![], @@ -2302,14 +2338,23 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - match channel_doc.permissions.get_mut(&permission) { - Some(authorized_roles) => { - authorized_roles.insert(role_id); - } - None => { - let mut roles = IndexSet::new(); - roles.insert(role_id); - channel_doc.permissions.insert(permission.clone(), roles); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); + } + for permission in &permissions { + match channel_doc.permissions.get_mut(permission) { + Some(authorized_roles) => { + authorized_roles.insert(role_id); + } + None => { + let mut roles = IndexSet::new(); + roles.insert(role_id); + channel_doc.permissions.insert(permission.clone(), roles); + } } } self.set_document().await?; @@ -2319,7 +2364,7 @@ impl CommunityTask { .send(MessageEventKind::GrantedCommunityChannelPermission { community_id: self.community_id, channel_id, - permission: permission.clone(), + permissions: permissions.clone(), role_id, }); @@ -2329,7 +2374,7 @@ impl CommunityTask { community: self.document.clone(), kind: CommunityUpdateKind::GrantCommunityChannelPermission { channel_id, - permission, + permissions, role_id, }, }, @@ -2357,8 +2402,17 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - if let Some(authorized_roles) = channel_doc.permissions.get_mut(&permission) { - authorized_roles.swap_remove(&role_id); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); + } + for permission in &permissions { + if let Some(authorized_roles) = channel_doc.permissions.get_mut(permission) { + authorized_roles.swap_remove(&role_id); + } } self.set_document().await?; @@ -2367,7 +2421,7 @@ impl CommunityTask { .send(MessageEventKind::RevokedCommunityChannelPermission { community_id: self.community_id, channel_id, - permission: permission.clone(), + permissions: permissions.clone(), role_id, }); @@ -2377,7 +2431,7 @@ impl CommunityTask { community: self.document.clone(), kind: CommunityUpdateKind::RevokeCommunityChannelPermission { channel_id, - permission, + permissions, role_id, }, }, @@ -2404,19 +2458,27 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - if channel_doc.permissions.contains_key(&permission) { - channel_doc.permissions.swap_remove(&permission); - self.set_document().await?; - } else { - return Err(Error::PermissionAlreadyGranted); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); } - + for permission in &permissions { + if channel_doc.permissions.contains_key(permission) { + channel_doc.permissions.swap_remove(permission); + } else if permissions.len() == 1 { + return Err(Error::PermissionAlreadyGranted); + } + } + self.set_document().await?; let _ = self.event_broadcast .send(MessageEventKind::GrantedCommunityChannelPermissionForAll { community_id: self.community_id, channel_id, - permission: permission.clone(), + permissions: permissions.clone(), }); self.publish( @@ -2425,7 +2487,7 @@ impl CommunityTask { community: self.document.clone(), kind: CommunityUpdateKind::GrantCommunityChannelPermissionForAll { channel_id, - permission, + permissions, }, }, true, @@ -2451,9 +2513,18 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - channel_doc - .permissions - .insert(permission.clone(), IndexSet::new()); + let permissions: Vec = CommunityPermission::sub_permissions(&permission) + .iter() + .map(|p| p.to_string()) + .collect(); + if permission.is_empty() { + return Err(Error::InvalidPermission); + } + for permission in &permissions { + channel_doc + .permissions + .insert(permission.clone(), IndexSet::new()); + } self.set_document().await?; let _ = @@ -2461,7 +2532,7 @@ impl CommunityTask { .send(MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: self.community_id, channel_id, - permission: permission.clone(), + permissions: permissions.clone(), }); self.publish( @@ -2470,7 +2541,7 @@ impl CommunityTask { community: self.document.clone(), kind: CommunityUpdateKind::RevokeCommunityChannelPermissionForAll { channel_id, - permission, + permissions, }, }, true, @@ -3011,7 +3082,7 @@ async fn message_event( } } CommunityUpdateKind::GrantCommunityPermission { - permission, + permissions, role_id, } => { this.replace_document(community).await?; @@ -3019,7 +3090,7 @@ async fn message_event( this.event_broadcast .send(MessageEventKind::GrantedCommunityPermission { community_id, - permission, + permissions, role_id, }) { @@ -3027,7 +3098,7 @@ async fn message_event( } } CommunityUpdateKind::RevokeCommunityPermission { - permission, + permissions, role_id, } => { this.replace_document(community).await?; @@ -3035,30 +3106,30 @@ async fn message_event( this.event_broadcast .send(MessageEventKind::RevokedCommunityPermission { community_id, - permission, + permissions, role_id, }) { tracing::warn!(%community_id, error = %e, "Error broadcasting event"); } } - CommunityUpdateKind::GrantCommunityPermissionForAll { permission } => { + CommunityUpdateKind::GrantCommunityPermissionForAll { permissions } => { this.replace_document(community).await?; if let Err(e) = this.event_broadcast.send( MessageEventKind::GrantedCommunityPermissionForAll { community_id, - permission, + permissions, }, ) { tracing::warn!(%community_id, error = %e, "Error broadcasting event"); } } - CommunityUpdateKind::RevokeCommunityPermissionForAll { permission } => { + CommunityUpdateKind::RevokeCommunityPermissionForAll { permissions } => { this.replace_document(community).await?; if let Err(e) = this.event_broadcast.send( MessageEventKind::RevokedCommunityPermissionForAll { community_id, - permission, + permissions, }, ) { tracing::warn!(%community_id, error = %e, "Error broadcasting event"); @@ -3106,7 +3177,7 @@ async fn message_event( } CommunityUpdateKind::GrantCommunityChannelPermission { channel_id, - permission, + permissions, role_id, } => { this.replace_document(community).await?; @@ -3114,7 +3185,7 @@ async fn message_event( MessageEventKind::GrantedCommunityChannelPermission { community_id, channel_id, - permission, + permissions, role_id, }, ) { @@ -3123,7 +3194,7 @@ async fn message_event( } CommunityUpdateKind::RevokeCommunityChannelPermission { channel_id, - permission, + permissions, role_id, } => { this.replace_document(community).await?; @@ -3131,7 +3202,7 @@ async fn message_event( MessageEventKind::RevokedCommunityChannelPermission { community_id, channel_id, - permission, + permissions, role_id, }, ) { @@ -3140,14 +3211,14 @@ async fn message_event( } CommunityUpdateKind::GrantCommunityChannelPermissionForAll { channel_id, - permission, + permissions, } => { this.replace_document(community).await?; if let Err(e) = this.event_broadcast.send( MessageEventKind::GrantedCommunityChannelPermissionForAll { community_id, channel_id, - permission, + permissions, }, ) { tracing::warn!(%community_id, error = %e, "Error broadcasting event"); @@ -3155,14 +3226,14 @@ async fn message_event( } CommunityUpdateKind::RevokeCommunityChannelPermissionForAll { channel_id, - permission, + permissions, } => { this.replace_document(community).await?; if let Err(e) = this.event_broadcast.send( MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id, channel_id, - permission, + permissions, }, ) { tracing::warn!(%community_id, error = %e, "Error broadcasting event"); diff --git a/extensions/warp-ipfs/src/store/mod.rs b/extensions/warp-ipfs/src/store/mod.rs index 8be804d69..241d84547 100644 --- a/extensions/warp-ipfs/src/store/mod.rs +++ b/extensions/warp-ipfs/src/store/mod.rs @@ -500,18 +500,18 @@ pub enum CommunityUpdateKind { description: Option, }, GrantCommunityPermission { - permission: String, + permissions: Vec, role_id: RoleId, }, RevokeCommunityPermission { - permission: String, + permissions: Vec, role_id: RoleId, }, GrantCommunityPermissionForAll { - permission: String, + permissions: Vec, }, RevokeCommunityPermissionForAll { - permission: String, + permissions: Vec, }, RemoveCommunityMember { member: DID, @@ -526,21 +526,21 @@ pub enum CommunityUpdateKind { }, GrantCommunityChannelPermission { channel_id: Uuid, - permission: String, + permissions: Vec, role_id: RoleId, }, RevokeCommunityChannelPermission { channel_id: Uuid, - permission: String, + permissions: Vec, role_id: RoleId, }, GrantCommunityChannelPermissionForAll { channel_id: Uuid, - permission: String, + permissions: Vec, }, RevokeCommunityChannelPermissionForAll { channel_id: Uuid, - permission: String, + permissions: Vec, }, } diff --git a/warp/src/error.rs b/warp/src/error.rs index 713fa8ca9..265d4fe83 100644 --- a/warp/src/error.rs +++ b/warp/src/error.rs @@ -268,6 +268,8 @@ pub enum Error { //Misc #[error("Unauthorized")] Unauthorized, + #[error("Invalid permission")] + InvalidPermission, #[error("Length for '{context}' is invalid. Current length: {current}. Minimum Length: {minimum:?}, Maximum: {maximum:?}")] InvalidLength { context: String, diff --git a/warp/src/raygun/community.rs b/warp/src/raygun/community.rs index 941e144dd..e6b043210 100644 --- a/warp/src/raygun/community.rs +++ b/warp/src/raygun/community.rs @@ -312,6 +312,14 @@ impl CommunityPermission { CommunityPermission::DeleteMessages, ] } + + pub fn sub_permissions(node: &str) -> Vec { + CommunityPermission::values() + .iter() + .filter(|p| p.to_string().starts_with(node)) + .cloned() + .collect() + } } /// Built-in CommunityChannelPermission for ease of access @@ -323,6 +331,16 @@ pub enum CommunityChannelPermission { SendMessages, } +impl CommunityChannelPermission { + pub fn sub_permissions(node: &str) -> Vec { + CommunityChannelPermission::values() + .iter() + .filter(|p| p.to_string().starts_with(node)) + .cloned() + .collect() + } +} + #[async_trait::async_trait] pub trait RayGunCommunity: Sync + Send { async fn get_community_stream( diff --git a/warp/src/raygun/mod.rs b/warp/src/raygun/mod.rs index 0e3ed5430..08e6f39b8 100644 --- a/warp/src/raygun/mod.rs +++ b/warp/src/raygun/mod.rs @@ -187,21 +187,21 @@ pub enum MessageEventKind { }, GrantedCommunityPermission { community_id: Uuid, - permission: String, + permissions: Vec, role_id: RoleId, }, RevokedCommunityPermission { community_id: Uuid, - permission: String, + permissions: Vec, role_id: RoleId, }, GrantedCommunityPermissionForAll { community_id: Uuid, - permission: String, + permissions: Vec, }, RevokedCommunityPermissionForAll { community_id: Uuid, - permission: String, + permissions: Vec, }, RemovedCommunityMember { community_id: Uuid, @@ -220,24 +220,24 @@ pub enum MessageEventKind { GrantedCommunityChannelPermission { community_id: Uuid, channel_id: Uuid, - permission: String, + permissions: Vec, role_id: RoleId, }, RevokedCommunityChannelPermission { community_id: Uuid, channel_id: Uuid, - permission: String, + permissions: Vec, role_id: RoleId, }, GrantedCommunityChannelPermissionForAll { community_id: Uuid, channel_id: Uuid, - permission: String, + permissions: Vec, }, RevokedCommunityChannelPermissionForAll { community_id: Uuid, channel_id: Uuid, - permission: String, + permissions: Vec, }, } From 803eef5a797cdc565473312658791bdd3b800fbd Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Fri, 22 Nov 2024 17:15:06 +0100 Subject: [PATCH 07/15] add has_permission api call for community --- extensions/warp-ipfs/src/lib.rs | 32 +++++++++++++ extensions/warp-ipfs/src/store/message.rs | 48 +++++++++++++++++++ .../src/store/message/community_task.rs | 47 ++++++++++++++++++ warp/src/raygun/community.rs | 23 +++++++++ warp/src/warp.rs | 27 +++++++++++ 5 files changed, 177 insertions(+) diff --git a/extensions/warp-ipfs/src/lib.rs b/extensions/warp-ipfs/src/lib.rs index 6b82a6d26..f71a6708d 100644 --- a/extensions/warp-ipfs/src/lib.rs +++ b/extensions/warp-ipfs/src/lib.rs @@ -1908,6 +1908,19 @@ impl RayGunCommunity for WarpIpfs { .revoke_community_permission_for_all(community_id, permission.to_string()) .await } + async fn has_community_permission( + &mut self, + community_id: Uuid, + permission: T, + member: DID, + ) -> Result + where + T: ToString + Send, + { + self.messaging_store()? + .has_community_permission(community_id, permission.to_string(), member) + .await + } async fn remove_community_member( &mut self, community_id: Uuid, @@ -2010,6 +2023,25 @@ impl RayGunCommunity for WarpIpfs { ) .await } + async fn has_community_channel_permission( + &mut self, + community_id: Uuid, + channel_id: Uuid, + permission: T, + member: DID, + ) -> Result + where + T: ToString + Send, + { + self.messaging_store()? + .has_community_channel_permission( + community_id, + channel_id, + permission.to_string(), + member, + ) + .await + } async fn get_community_channel_message( &self, diff --git a/extensions/warp-ipfs/src/store/message.rs b/extensions/warp-ipfs/src/store/message.rs index d880db7aa..c3712cf42 100644 --- a/extensions/warp-ipfs/src/store/message.rs +++ b/extensions/warp-ipfs/src/store/message.rs @@ -1435,6 +1435,29 @@ impl MessageStore { .await; rx.await.map_err(anyhow::Error::from)? } + pub async fn has_community_permission( + &mut self, + community_id: Uuid, + permission: String, + member: DID, + ) -> Result { + let inner = &*self.inner.read().await; + let community_meta = inner + .community_task + .get(&community_id) + .ok_or(Error::InvalidCommunity)?; + let (tx, rx) = oneshot::channel(); + let _ = community_meta + .command_tx + .clone() + .send(CommunityTaskCommand::HasCommunityPermission { + permission, + member, + response: tx, + }) + .await; + rx.await.map_err(anyhow::Error::from)? + } pub async fn revoke_community_permission_for_all( &mut self, community_id: Uuid, @@ -1624,6 +1647,31 @@ impl MessageStore { .await; rx.await.map_err(anyhow::Error::from)? } + pub async fn has_community_channel_permission( + &mut self, + community_id: Uuid, + channel_id: Uuid, + permission: String, + member: DID, + ) -> Result { + let inner = &*self.inner.read().await; + let community_meta = inner + .community_task + .get(&community_id) + .ok_or(Error::InvalidCommunity)?; + let (tx, rx) = oneshot::channel(); + let _ = community_meta + .command_tx + .clone() + .send(CommunityTaskCommand::HasCommunityChannelPermission { + channel_id, + permission, + member, + response: tx, + }) + .await; + rx.await.map_err(anyhow::Error::from)? + } pub async fn get_community_channel_message( &self, diff --git a/extensions/warp-ipfs/src/store/message/community_task.rs b/extensions/warp-ipfs/src/store/message/community_task.rs index 2efa9c103..2593f532c 100644 --- a/extensions/warp-ipfs/src/store/message/community_task.rs +++ b/extensions/warp-ipfs/src/store/message/community_task.rs @@ -177,6 +177,11 @@ pub enum CommunityTaskCommand { permission: String, response: oneshot::Sender>, }, + HasCommunityPermission { + permission: String, + member: DID, + response: oneshot::Sender>, + }, RemoveCommunityMember { member: DID, response: oneshot::Sender>, @@ -213,6 +218,12 @@ pub enum CommunityTaskCommand { permission: String, response: oneshot::Sender>, }, + HasCommunityChannelPermission { + channel_id: Uuid, + permission: String, + member: DID, + response: oneshot::Sender>, + }, GetCommunityChannelMessage { channel_id: Uuid, message_id: Uuid, @@ -846,6 +857,14 @@ impl CommunityTask { let result = self.revoke_community_permission_for_all(permission).await; let _ = response.send(result); } + CommunityTaskCommand::HasCommunityPermission { + response, + permission, + member, + } => { + let result = self.has_community_permission(permission, member).await; + let _ = response.send(result); + } CommunityTaskCommand::RemoveCommunityMember { response, member } => { let result = self.remove_community_member(member).await; let _ = response.send(result); @@ -910,6 +929,17 @@ impl CommunityTask { .await; let _ = response.send(result); } + CommunityTaskCommand::HasCommunityChannelPermission { + response, + channel_id, + permission, + member, + } => { + let result = self + .has_community_channel_permission(channel_id, permission, member) + .await; + let _ = response.send(result); + } CommunityTaskCommand::GetCommunityChannelMessage { channel_id, message_id, @@ -2199,6 +2229,13 @@ impl CommunityTask { ) .await } + pub async fn has_community_permission( + &mut self, + permission: String, + member: DID, + ) -> Result { + Ok(self.document.has_permission(&member, &permission)) + } pub async fn remove_community_member(&mut self, member: DID) -> Result<(), Error> { let own_did = &self.identity.did_key(); if !self @@ -2549,6 +2586,16 @@ impl CommunityTask { ) .await } + pub async fn has_community_channel_permission( + &mut self, + channel_id: Uuid, + permission: String, + member: DID, + ) -> Result { + Ok(self + .document + .has_channel_permission(&member, &permission, channel_id)) + } pub async fn get_community_channel_message( &self, diff --git a/warp/src/raygun/community.rs b/warp/src/raygun/community.rs index e6b043210..bd1d774d3 100644 --- a/warp/src/raygun/community.rs +++ b/warp/src/raygun/community.rs @@ -550,6 +550,17 @@ pub trait RayGunCommunity: Sync + Send { { Err(Error::Unimplemented) } + async fn has_community_permission( + &mut self, + _community_id: Uuid, + _permission: T, + _member: DID, + ) -> Result + where + T: ToString + Send, + { + Err(Error::Unimplemented) + } async fn remove_community_member( &mut self, _community_id: Uuid, @@ -620,6 +631,18 @@ pub trait RayGunCommunity: Sync + Send { { Err(Error::Unimplemented) } + async fn has_community_channel_permission( + &mut self, + _community_id: Uuid, + _channel_id: Uuid, + _permission: T, + _member: DID, + ) -> Result + where + T: ToString + Send, + { + Err(Error::Unimplemented) + } /// Retrieve all messages from a conversation async fn get_community_channel_message( diff --git a/warp/src/warp.rs b/warp/src/warp.rs index a6ae13ecf..0547219b1 100644 --- a/warp/src/warp.rs +++ b/warp/src/warp.rs @@ -813,6 +813,19 @@ where .revoke_community_permission_for_all(community_id, permission) .await } + async fn has_community_permission( + &mut self, + community_id: Uuid, + permission: T, + member: DID, + ) -> Result + where + T: ToString + Send, + { + self.raygun + .has_community_permission(community_id, permission, member) + .await + } async fn remove_community_member( &mut self, community_id: Uuid, @@ -897,6 +910,20 @@ where .revoke_community_channel_permission_for_all(community_id, channel_id, permission) .await } + async fn has_community_channel_permission( + &mut self, + community_id: Uuid, + channel_id: Uuid, + permission: T, + member: DID, + ) -> Result + where + T: ToString + Send, + { + self.raygun + .has_community_channel_permission(community_id, channel_id, permission, member) + .await + } async fn get_community_channel_message( &self, From 5f09d14af388a80a2ad1a6337732a5590b402b0c Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Mon, 25 Nov 2024 14:05:10 +0100 Subject: [PATCH 08/15] fix wrong permission for channel --- .../src/store/message/community_task.rs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/extensions/warp-ipfs/src/store/message/community_task.rs b/extensions/warp-ipfs/src/store/message/community_task.rs index 2593f532c..4da81a424 100644 --- a/extensions/warp-ipfs/src/store/message/community_task.rs +++ b/extensions/warp-ipfs/src/store/message/community_task.rs @@ -2052,7 +2052,7 @@ impl CommunityTask { .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { @@ -2108,7 +2108,7 @@ impl CommunityTask { .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { @@ -2155,7 +2155,7 @@ impl CommunityTask { .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { @@ -2201,7 +2201,7 @@ impl CommunityTask { .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { @@ -2375,11 +2375,11 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - let permissions: Vec = CommunityPermission::sub_permissions(&permission) + let permissions: Vec = CommunityChannelPermission::sub_permissions(&permission) .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { @@ -2439,11 +2439,11 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - let permissions: Vec = CommunityPermission::sub_permissions(&permission) + let permissions: Vec = CommunityChannelPermission::sub_permissions(&permission) .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { @@ -2495,11 +2495,11 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - let permissions: Vec = CommunityPermission::sub_permissions(&permission) + let permissions: Vec = CommunityChannelPermission::sub_permissions(&permission) .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { @@ -2550,11 +2550,11 @@ impl CommunityTask { .channels .get_mut(&channel_id.to_string()) .ok_or(Error::CommunityChannelDoesntExist)?; - let permissions: Vec = CommunityPermission::sub_permissions(&permission) + let permissions: Vec = CommunityChannelPermission::sub_permissions(&permission) .iter() .map(|p| p.to_string()) .collect(); - if permission.is_empty() { + if permissions.is_empty() { return Err(Error::InvalidPermission); } for permission in &permissions { From 32812980097a3d8d41db9eec4344bb9fd5a5e895 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Tue, 26 Nov 2024 15:48:52 +0100 Subject: [PATCH 09/15] clippy --- extensions/warp-ipfs/src/store/community.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index 9480400bb..87a7f5e1b 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -22,7 +22,7 @@ use warp::{ error::Error, raygun::{ community::{ - Community, CommunityChannel, CommunityChannelPermission, CommunityChannelPermissions, + Community, CommunityChannel, CommunityChannelPermissions, CommunityChannelType, CommunityInvite, CommunityPermission, CommunityPermissions, CommunityRole, RoleId, }, From b9fc066ac517da27a1489476c394379576e7b995 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Thu, 28 Nov 2024 18:49:23 +0100 Subject: [PATCH 10/15] change back to serde --- extensions/warp-ipfs/src/store/community.rs | 5 +- tools/enum_macro/src/lib.rs | 80 --------------------- warp/src/raygun/community.rs | 61 +++++++++------- 3 files changed, 38 insertions(+), 108 deletions(-) diff --git a/extensions/warp-ipfs/src/store/community.rs b/extensions/warp-ipfs/src/store/community.rs index 87a7f5e1b..196643e1b 100644 --- a/extensions/warp-ipfs/src/store/community.rs +++ b/extensions/warp-ipfs/src/store/community.rs @@ -22,9 +22,8 @@ use warp::{ error::Error, raygun::{ community::{ - Community, CommunityChannel, CommunityChannelPermissions, - CommunityChannelType, CommunityInvite, CommunityPermission, CommunityPermissions, - CommunityRole, RoleId, + Community, CommunityChannel, CommunityChannelPermissions, CommunityChannelType, + CommunityInvite, CommunityPermission, CommunityPermissions, CommunityRole, RoleId, }, Message, MessageOptions, MessagePage, MessageReference, Messages, MessagesType, }, diff --git a/tools/enum_macro/src/lib.rs b/tools/enum_macro/src/lib.rs index 930162f2d..2f38dee8b 100644 --- a/tools/enum_macro/src/lib.rs +++ b/tools/enum_macro/src/lib.rs @@ -4,86 +4,6 @@ use proc_macro::TokenStream; use quote::quote; use syn::{parse_macro_input, Data, DeriveInput}; -/// Implements Display for enum values with formatting and snake_case conversion -/// This is used for CommunityPermissions -/// E.g. -/// ``` -/// use enum_macro::EnumFormatting; -/// #[derive(EnumFormatting)] -/// pub enum SomeEnum { -/// #[permission(formatting="prefix.{}")] -/// SomeValue1, -/// SomeValue2 -/// } -/// -/// assert_eq!(SomeEnum::SomeValue1.to_string(), "prefix.some_value1"); -/// assert_eq!(SomeEnum::SomeValue2.to_string(), "some_value2"); -/// ``` -#[proc_macro_derive(EnumFormatting, attributes(permission))] -pub fn permission_node(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let name = &input.ident; - if let Data::Enum(enum_data) = &input.data { - let mut variants = vec![]; - for variant in &enum_data.variants { - let mut format = None; - if let Some(attr) = variant - .attrs - .iter() - .find(|attr| attr.path().is_ident("permission")) - { - if let syn::Meta::List(l) = &attr.meta { - if let Err(err) = l.parse_nested_meta(|meta| { - if meta.path.is_ident("formatting") { - let exp: syn::Expr = meta.value()?.parse()?; - if let syn::Expr::Lit(v) = exp { - if let syn::Lit::Str(s) = v.lit { - format = Some(s.value()); - } - } - return Ok(()); - } - Err(meta.error("Unrecognized attribute")) - }) { - panic!("{:?}", err); - } - } - } - let format = format.unwrap_or("{}".into()); - - let variant_name = &variant.ident; - - variants.push(quote! { - #name::#variant_name => { - let mut snake = String::new(); - for (i, ch) in stringify!(#variant_name).char_indices() { - if i > 0 && ch.is_uppercase() { - snake.push('_'); - } - snake.push(ch.to_ascii_lowercase()); - } - format!(#format, snake) - }, - }); - } - - let expanded = quote! { - impl std::fmt::Display for #name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let result = match self { - #(#variants)* - }; - f.write_str(&format!("{}", result)) - } - } - }; - - TokenStream::from(expanded) - } else { - panic!("Only Enums are supported"); - } -} - /// Implements #values() for enum values that returns all defined values in the enum /// E.g. /// ``` diff --git a/warp/src/raygun/community.rs b/warp/src/raygun/community.rs index bd1d774d3..db4bb6875 100644 --- a/warp/src/raygun/community.rs +++ b/warp/src/raygun/community.rs @@ -15,7 +15,7 @@ use super::{ AttachmentEventStream, ConversationImage, Message, MessageEvent, MessageEventStream, MessageOptions, MessageReference, MessageStatus, Messages, PinState, ReactionState, }; -use enum_macro::{EnumFormatting, EnumValues}; +use enum_macro::EnumValues; pub type RoleId = Uuid; pub type CommunityRoles = IndexMap; @@ -238,56 +238,61 @@ pub enum CommunityChannelType { VoiceEnabled, } -/// Built-in CommunityPermission for ease of access -#[derive(Debug, Copy, Clone, EnumFormatting, EnumValues, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, EnumValues, PartialEq, Eq, Hash)] pub enum CommunityPermission { - #[permission(formatting = "community.visuals.{}")] + #[serde(rename = "community.visuals.edit_name")] EditName, - #[permission(formatting = "community.visuals.{}")] + #[serde(rename = "community.visuals.edit_description")] EditDescription, - #[permission(formatting = "community.visuals.{}")] + #[serde(rename = "community.visuals.edit_icon")] EditIcon, - #[permission(formatting = "community.visuals.{}")] + #[serde(rename = "community.visuals.edit_banner")] EditBanner, - #[permission(formatting = "community.roles.{}")] + #[serde(rename = "community.roles.create_roles")] CreateRoles, - #[permission(formatting = "community.roles.{}")] + #[serde(rename = "community.roles.edit_roles")] EditRoles, - #[permission(formatting = "community.roles.{}")] + #[serde(rename = "community.roles.delete_roles")] DeleteRoles, - #[permission(formatting = "community.roles.{}")] + #[serde(rename = "community.roles.grant_roles")] GrantRoles, - #[permission(formatting = "community.roles.{}")] + #[serde(rename = "community.roles.revoke_roles")] RevokeRoles, - #[permission(formatting = "community.permissions.{}")] + #[serde(rename = "community.permissions.grant_permissions")] GrantPermissions, - #[permission(formatting = "community.permissions.{}")] + #[serde(rename = "community.permissions.revoke_permissions")] RevokePermissions, - #[permission(formatting = "community.invites.{}")] + #[serde(rename = "community.invites.create_invites")] CreateInvites, - #[permission(formatting = "community.invites.{}")] + #[serde(rename = "community.invites.edit_invites")] EditInvites, - #[permission(formatting = "community.invites.{}")] + #[serde(rename = "community.invites.delete_invites")] DeleteInvites, - #[permission(formatting = "community.channels.{}")] + #[serde(rename = "community.channels.create_channels")] CreateChannels, - #[permission(formatting = "community.channels.{}")] + #[serde(rename = "community.channels.edit_channels")] EditChannels, - #[permission(formatting = "community.channels.{}")] + #[serde(rename = "community.channels.delete_channels")] DeleteChannels, - #[permission(formatting = "community.members.{}")] + #[serde(rename = "community.members.remove_members")] RemoveMembers, - #[permission(formatting = "community.messages.{}")] + #[serde(rename = "community.messages.delete_messages")] DeleteMessages, } +impl ToString for CommunityPermission { + fn to_string(&self) -> String { + serde_json::to_string(self).unwrap() + } +} + impl CommunityPermission { pub fn default_disabled() -> Vec { vec![ @@ -323,14 +328,20 @@ impl CommunityPermission { } /// Built-in CommunityChannelPermission for ease of access -#[derive(Debug, Copy, Clone, EnumFormatting, EnumValues, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Serialize, Deserialize, EnumValues, PartialEq, Eq, Hash)] pub enum CommunityChannelPermission { - #[permission(formatting = "community_channel.{}")] + #[serde(rename = "community_channel.view_channel")] ViewChannel, - #[permission(formatting = "community_channel.{}")] + #[serde(rename = "community_channel.messages.send_messages")] SendMessages, } +impl ToString for CommunityChannelPermission { + fn to_string(&self) -> String { + serde_json::to_string(self).unwrap() + } +} + impl CommunityChannelPermission { pub fn sub_permissions(node: &str) -> Vec { CommunityChannelPermission::values() From ef44f59f42dbd79fc8a94e744b078d9eba7d5cfc Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Fri, 29 Nov 2024 14:48:00 +0100 Subject: [PATCH 11/15] change to display --- warp/src/raygun/community.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/warp/src/raygun/community.rs b/warp/src/raygun/community.rs index 9f5b26e56..2581c5b73 100644 --- a/warp/src/raygun/community.rs +++ b/warp/src/raygun/community.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::path::PathBuf; use bytes::Bytes; @@ -289,9 +290,9 @@ pub enum CommunityPermission { PinMessages, } -impl ToString for CommunityPermission { - fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() +impl Display for CommunityPermission { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&serde_json::to_string(self).unwrap()) } } @@ -339,9 +340,9 @@ pub enum CommunityChannelPermission { SendMessages, } -impl ToString for CommunityChannelPermission { - fn to_string(&self) -> String { - serde_json::to_string(self).unwrap() +impl Display for CommunityChannelPermission { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&serde_json::to_string(self).unwrap()) } } From 3ff873b1c195af0c61978d24e1487f4c79201c44 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Fri, 29 Nov 2024 19:03:08 +0100 Subject: [PATCH 12/15] linter --- warp/src/constellation/directory.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/warp/src/constellation/directory.rs b/warp/src/constellation/directory.rs index 249e6ec92..928c55c5a 100644 --- a/warp/src/constellation/directory.rs +++ b/warp/src/constellation/directory.rs @@ -516,7 +516,7 @@ impl Directory { } let name = path.remove(0); let item = self.get_item(name)?; - return match item { + match item { Item::Directory(dir) => { if path.is_empty() { return Ok(dir); @@ -526,7 +526,7 @@ impl Directory { .unwrap_or(dir)); } _ => Err(Error::DirectoryNotFound), - }; + } } /// Get an `Item` from a path @@ -558,7 +558,7 @@ impl Directory { } let name = path.remove(0); let item = self.get_item(name)?; - return match &item { + match &item { Item::Directory(dir) => { if path.is_empty() { return Ok(item); @@ -566,7 +566,7 @@ impl Directory { return dir.get_item_by_path(path.join("/").as_str()); } _ => Ok(item), - }; + } } } From db11e3dfc1389b1e0f30eb8cc8f8e357236f5d72 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Fri, 29 Nov 2024 19:16:49 +0100 Subject: [PATCH 13/15] linter --- warp/src/constellation/directory.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/warp/src/constellation/directory.rs b/warp/src/constellation/directory.rs index 928c55c5a..83fd935b0 100644 --- a/warp/src/constellation/directory.rs +++ b/warp/src/constellation/directory.rs @@ -521,9 +521,9 @@ impl Directory { if path.is_empty() { return Ok(dir); } - return Ok(dir + Ok(dir .get_last_directory_from_path(path.join("/").as_str()) - .unwrap_or(dir)); + .unwrap_or(dir)) } _ => Err(Error::DirectoryNotFound), } @@ -563,7 +563,7 @@ impl Directory { if path.is_empty() { return Ok(item); } - return dir.get_item_by_path(path.join("/").as_str()); + dir.get_item_by_path(path.join("/").as_str()) } _ => Ok(item), } From 64934cfa1a8c02f57d205a6ee10bbc7a5f215885 Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Mon, 2 Dec 2024 17:29:15 +0100 Subject: [PATCH 14/15] fix tests --- extensions/warp-ipfs/tests/community.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/warp-ipfs/tests/community.rs b/extensions/warp-ipfs/tests/community.rs index ce8eeec00..52c15e658 100644 --- a/extensions/warp-ipfs/tests/community.rs +++ b/extensions/warp-ipfs/tests/community.rs @@ -3416,7 +3416,7 @@ mod test { MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: community.id(), channel_id: channel.id(), - permission: CommunityChannelPermission::SendMessages, + permissions: vec![CommunityChannelPermission::SendMessages.to_string()], }, ) .await?; @@ -3608,7 +3608,7 @@ mod test { MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: community.id(), channel_id: channel.id(), - permission: CommunityChannelPermission::SendMessages, + permissions: vec![CommunityChannelPermission::SendMessages.to_string()], }, ) .await?; @@ -3993,7 +3993,7 @@ mod test { Duration::from_secs(60), MessageEventKind::RevokedCommunityPermissionForAll { community_id: community.id(), - permission: CommunityPermission::PinMessages, + permissions: vec![CommunityPermission::PinMessages.to_string()], }, ) .await?; @@ -4193,7 +4193,7 @@ mod test { MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: community.id(), channel_id: channel.id(), - permission: CommunityChannelPermission::ViewChannel, + permissions: vec![CommunityChannelPermission::ViewChannel.to_string()], }, ) .await?; From 7aa601107e6252b0bdccdd4ed68bbc984a1c8a9c Mon Sep 17 00:00:00 2001 From: Flemmli97 Date: Fri, 6 Dec 2024 16:22:53 +0100 Subject: [PATCH 15/15] fix test --- extensions/warp-ipfs/tests/community.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/warp-ipfs/tests/community.rs b/extensions/warp-ipfs/tests/community.rs index f69bce2ff..1e97e2279 100644 --- a/extensions/warp-ipfs/tests/community.rs +++ b/extensions/warp-ipfs/tests/community.rs @@ -4570,7 +4570,7 @@ mod test { MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: community.id(), channel_id: channel.id(), - permission: CommunityChannelPermission::SendAttachments, + permissions: vec![CommunityChannelPermission::SendAttachments.to_string()], }, ) .await?; @@ -4743,7 +4743,7 @@ mod test { MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: community.id(), channel_id: channel.id(), - permission: CommunityChannelPermission::ViewChannel, + permissions: vec![CommunityChannelPermission::ViewChannel.to_string()], }, ) .await?; @@ -4971,7 +4971,7 @@ mod test { MessageEventKind::RevokedCommunityChannelPermissionForAll { community_id: community.id(), channel_id: channel.id(), - permission: CommunityChannelPermission::ViewChannel, + permissions: vec![CommunityChannelPermission::ViewChannel.to_string()], }, ) .await?;