From 4336127769cbb7b5dc13595a465624fa7b123541 Mon Sep 17 00:00:00 2001 From: Ry Racherbaumer Date: Fri, 13 Dec 2024 17:38:49 -0600 Subject: [PATCH] Added support for custom permissions policy (WASM + Node bindings) (#1414) * Add custom permissions policy to Node bindings * Add custom permissions policy to WASM bindings * Prepare node bindings release * Prepare WASM bindings release * Add test * Support permissions policy updates in WASM bindings * Support permissions policy updates in Node bindings * Add test --- bindings_node/CHANGELOG.md | 4 + bindings_node/package.json | 2 +- bindings_node/src/conversation.rs | 31 +++++- bindings_node/src/conversations.rs | 29 +++++- bindings_node/src/permissions.rs | 123 +++++++++++++++++++++-- bindings_node/test/Conversations.test.ts | 93 +++++++++++++++++ bindings_wasm/CHANGELOG.md | 4 + bindings_wasm/package.json | 2 +- bindings_wasm/src/conversation.rs | 21 ++++ bindings_wasm/src/conversations.rs | 30 +++++- bindings_wasm/src/permissions.rs | 113 +++++++++++++++++++-- 11 files changed, 425 insertions(+), 27 deletions(-) diff --git a/bindings_node/CHANGELOG.md b/bindings_node/CHANGELOG.md index 3a46a8658..98fc50dc7 100644 --- a/bindings_node/CHANGELOG.md +++ b/bindings_node/CHANGELOG.md @@ -1,5 +1,9 @@ # @xmtp/node-bindings +## 0.0.29 + +- Added support for custom permission policy sets + ## 0.0.28 - Removed `is_installation_authorized` and `is_address_authorized` from `Client` diff --git a/bindings_node/package.json b/bindings_node/package.json index a8a0907b0..4bc65d3e6 100644 --- a/bindings_node/package.json +++ b/bindings_node/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/node-bindings", - "version": "0.0.28", + "version": "0.0.29", "repository": { "type": "git", "url": "git+https://git@github.com/xmtp/libxmtp.git", diff --git a/bindings_node/src/conversation.rs b/bindings_node/src/conversation.rs index 897662463..3cd1328be 100644 --- a/bindings_node/src/conversation.rs +++ b/bindings_node/src/conversation.rs @@ -9,6 +9,8 @@ use xmtp_cryptography::signature::ed25519_public_key_to_address; use xmtp_mls::{ groups::{ group_metadata::GroupMetadata as XmtpGroupMetadata, + group_mutable_metadata::MetadataField as XmtpMetadataField, + intents::PermissionUpdateType as XmtpPermissionUpdateType, members::PermissionLevel as XmtpPermissionLevel, MlsGroup, UpdateAdminListType, }, storage::{ @@ -23,7 +25,7 @@ use crate::{ consent_state::ConsentState, encoded_content::EncodedContent, message::{ListMessagesOptions, Message}, - permissions::GroupPermissions, + permissions::{GroupPermissions, MetadataField, PermissionPolicy, PermissionUpdateType}, streams::StreamCloser, ErrorWrapper, }; @@ -654,4 +656,31 @@ impl Conversation { Ok(group.dm_inbox_id().map_err(ErrorWrapper::from)?) } + + #[napi] + pub async fn update_permission_policy( + &self, + permission_update_type: PermissionUpdateType, + permission_policy_option: PermissionPolicy, + metadata_field: Option, + ) -> Result<()> { + let group = MlsGroup::new( + self.inner_client.clone(), + self.group_id.clone(), + self.created_at_ns, + ); + + group + .update_permission_policy( + XmtpPermissionUpdateType::from(&permission_update_type), + permission_policy_option + .try_into() + .map_err(ErrorWrapper::from)?, + metadata_field.map(|field| XmtpMetadataField::from(&field)), + ) + .await + .map_err(ErrorWrapper::from)?; + + Ok(()) + } } diff --git a/bindings_node/src/conversations.rs b/bindings_node/src/conversations.rs index f957a9807..2b0247bb2 100644 --- a/bindings_node/src/conversations.rs +++ b/bindings_node/src/conversations.rs @@ -12,7 +12,7 @@ use xmtp_mls::storage::group::GroupMembershipState as XmtpGroupMembershipState; use xmtp_mls::storage::group::GroupQueryArgs; use crate::message::Message; -use crate::permissions::GroupPermissionsOptions; +use crate::permissions::{GroupPermissionsOptions, PermissionPolicySet}; use crate::ErrorWrapper; use crate::{client::RustXmtpClient, conversation::Conversation, streams::StreamCloser}; @@ -105,6 +105,7 @@ pub struct CreateGroupOptions { pub group_image_url_square: Option, pub group_description: Option, pub group_pinned_frame_url: Option, + pub custom_permission_policy_set: Option, } impl CreateGroupOptions { @@ -143,9 +144,22 @@ impl Conversations { group_image_url_square: None, group_description: None, group_pinned_frame_url: None, + custom_permission_policy_set: None, }, }; + if let Some(GroupPermissionsOptions::CustomPolicy) = options.permissions { + if options.custom_permission_policy_set.is_none() { + return Err(Error::from_reason("CustomPolicy must include policy set")); + } + } else if options.custom_permission_policy_set.is_some() { + return Err(Error::from_reason( + "Only CustomPolicy may specify a policy set", + )); + } + + let metadata_options = options.clone().into_group_metadata_options(); + let group_permissions = match options.permissions { Some(GroupPermissionsOptions::AllMembers) => { Some(PreconfiguredPolicies::AllMembers.to_policy_set()) @@ -153,11 +167,20 @@ impl Conversations { Some(GroupPermissionsOptions::AdminOnly) => { Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()) } + Some(GroupPermissionsOptions::CustomPolicy) => { + if let Some(policy_set) = options.custom_permission_policy_set { + Some( + policy_set + .try_into() + .map_err(|e| Error::from_reason(format!("{}", e).as_str()))?, + ) + } else { + None + } + } _ => None, }; - let metadata_options = options.clone().into_group_metadata_options(); - let convo = if account_addresses.is_empty() { self .inner_client diff --git a/bindings_node/src/permissions.rs b/bindings_node/src/permissions.rs index 98e672951..3008c4b4e 100644 --- a/bindings_node/src/permissions.rs +++ b/bindings_node/src/permissions.rs @@ -1,10 +1,12 @@ -use napi::bindgen_prelude::{Error, Result}; +use napi::bindgen_prelude::Result; use napi_derive::napi; +use std::collections::HashMap; use xmtp_mls::groups::{ - group_mutable_metadata::MetadataField, + group_mutable_metadata::MetadataField as XmtpMetadataField, group_permissions::{ - BasePolicies, GroupMutablePermissions, MembershipPolicies, MetadataBasePolicies, - MetadataPolicies, PermissionsBasePolicies, PermissionsPolicies, + BasePolicies, GroupMutablePermissions, GroupMutablePermissionsError, MembershipPolicies, + MetadataBasePolicies, MetadataPolicies, PermissionsBasePolicies, PermissionsPolicies, + PolicySet, }, intents::{PermissionPolicyOption, PermissionUpdateType as XmtpPermissionUpdateType}, PreconfiguredPolicies, @@ -49,15 +51,15 @@ pub enum PermissionPolicy { } impl TryInto for PermissionPolicy { - type Error = Error; + type Error = GroupMutablePermissionsError; - fn try_into(self) -> Result { + fn try_into(self) -> std::result::Result { match self { PermissionPolicy::Allow => Ok(PermissionPolicyOption::Allow), PermissionPolicy::Deny => Ok(PermissionPolicyOption::Deny), PermissionPolicy::Admin => Ok(PermissionPolicyOption::AdminOnly), PermissionPolicy::SuperAdmin => Ok(PermissionPolicyOption::SuperAdminOnly), - _ => Err(Error::from_reason("InvalidPermissionPolicyOption")), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), } } } @@ -107,7 +109,49 @@ impl From<&PermissionsPolicies> for PermissionPolicy { } } +impl TryInto for PermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> std::result::Result { + match self { + PermissionPolicy::Allow => Ok(MetadataPolicies::allow()), + PermissionPolicy::Deny => Ok(MetadataPolicies::deny()), + PermissionPolicy::Admin => Ok(MetadataPolicies::allow_if_actor_admin()), + PermissionPolicy::SuperAdmin => Ok(MetadataPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl TryInto for PermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> std::result::Result { + match self { + PermissionPolicy::Deny => Ok(PermissionsPolicies::deny()), + PermissionPolicy::Admin => Ok(PermissionsPolicies::allow_if_actor_admin()), + PermissionPolicy::SuperAdmin => Ok(PermissionsPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl TryInto for PermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> std::result::Result { + match self { + PermissionPolicy::Allow => Ok(MembershipPolicies::allow()), + PermissionPolicy::Deny => Ok(MembershipPolicies::deny()), + PermissionPolicy::Admin => Ok(MembershipPolicies::allow_if_actor_admin()), + PermissionPolicy::SuperAdmin => Ok(MembershipPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + #[napi(object)] +#[derive(Clone)] pub struct PermissionPolicySet { pub add_member_policy: PermissionPolicy, pub remove_member_policy: PermissionPolicy, @@ -163,10 +207,67 @@ impl GroupPermissions { remove_member_policy: PermissionPolicy::from(&policy_set.remove_member_policy), add_admin_policy: PermissionPolicy::from(&policy_set.add_admin_policy), remove_admin_policy: PermissionPolicy::from(&policy_set.remove_admin_policy), - update_group_name_policy: get_policy(MetadataField::GroupName.as_str()), - update_group_description_policy: get_policy(MetadataField::Description.as_str()), - update_group_image_url_square_policy: get_policy(MetadataField::GroupImageUrlSquare.as_str()), - update_group_pinned_frame_url_policy: get_policy(MetadataField::GroupPinnedFrameUrl.as_str()), + update_group_name_policy: get_policy(XmtpMetadataField::GroupName.as_str()), + update_group_description_policy: get_policy(XmtpMetadataField::Description.as_str()), + update_group_image_url_square_policy: get_policy( + XmtpMetadataField::GroupImageUrlSquare.as_str(), + ), + update_group_pinned_frame_url_policy: get_policy( + XmtpMetadataField::GroupPinnedFrameUrl.as_str(), + ), + }) + } +} + +impl TryFrom for PolicySet { + type Error = GroupMutablePermissionsError; + fn try_from( + policy_set: PermissionPolicySet, + ) -> std::result::Result { + let mut metadata_permissions_map: HashMap = HashMap::new(); + metadata_permissions_map.insert( + XmtpMetadataField::GroupName.to_string(), + policy_set.update_group_name_policy.try_into()?, + ); + metadata_permissions_map.insert( + XmtpMetadataField::Description.to_string(), + policy_set.update_group_description_policy.try_into()?, + ); + metadata_permissions_map.insert( + XmtpMetadataField::GroupImageUrlSquare.to_string(), + policy_set.update_group_image_url_square_policy.try_into()?, + ); + metadata_permissions_map.insert( + XmtpMetadataField::GroupPinnedFrameUrl.to_string(), + policy_set.update_group_pinned_frame_url_policy.try_into()?, + ); + + Ok(PolicySet { + add_member_policy: policy_set.add_member_policy.try_into()?, + remove_member_policy: policy_set.remove_member_policy.try_into()?, + add_admin_policy: policy_set.add_admin_policy.try_into()?, + remove_admin_policy: policy_set.remove_admin_policy.try_into()?, + update_metadata_policy: metadata_permissions_map, + update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(), }) } } + +#[napi] +pub enum MetadataField { + GroupName, + Description, + ImageUrlSquare, + PinnedFrameUrl, +} + +impl From<&MetadataField> for XmtpMetadataField { + fn from(field: &MetadataField) -> Self { + match field { + MetadataField::GroupName => XmtpMetadataField::GroupName, + MetadataField::Description => XmtpMetadataField::Description, + MetadataField::ImageUrlSquare => XmtpMetadataField::GroupImageUrlSquare, + MetadataField::PinnedFrameUrl => XmtpMetadataField::GroupPinnedFrameUrl, + } + } +} diff --git a/bindings_node/test/Conversations.test.ts b/bindings_node/test/Conversations.test.ts index 951aa61df..ee40b431d 100644 --- a/bindings_node/test/Conversations.test.ts +++ b/bindings_node/test/Conversations.test.ts @@ -9,6 +9,9 @@ import { Conversation, GroupPermissionsOptions, Message, + MetadataField, + PermissionPolicy, + PermissionUpdateType, } from '../dist' const SLEEP_MS = 100 @@ -80,6 +83,96 @@ describe('Conversations', () => { expect((await client2.conversations().listGroups()).length).toBe(1) }) + it('should create a group with custom permissions', async () => { + const user1 = createUser() + const user2 = createUser() + const client1 = await createRegisteredClient(user1) + const client2 = await createRegisteredClient(user2) + const group = await client1 + .conversations() + .createGroup([user2.account.address], { + permissions: GroupPermissionsOptions.CustomPolicy, + customPermissionPolicySet: { + addAdminPolicy: 2, + addMemberPolicy: 3, + removeAdminPolicy: 1, + removeMemberPolicy: 0, + updateGroupNamePolicy: 2, + updateGroupDescriptionPolicy: 1, + updateGroupImageUrlSquarePolicy: 0, + updateGroupPinnedFrameUrlPolicy: 3, + }, + }) + expect(group).toBeDefined() + expect(group.groupPermissions().policyType()).toBe( + GroupPermissionsOptions.CustomPolicy + ) + expect(group.groupPermissions().policySet()).toEqual({ + addAdminPolicy: 2, + addMemberPolicy: 3, + removeAdminPolicy: 1, + removeMemberPolicy: 0, + updateGroupNamePolicy: 2, + updateGroupDescriptionPolicy: 1, + updateGroupImageUrlSquarePolicy: 0, + updateGroupPinnedFrameUrlPolicy: 3, + }) + }) + + it('should update group permission policy', async () => { + const user1 = createUser() + const user2 = createUser() + const client1 = await createRegisteredClient(user1) + const client2 = await createRegisteredClient(user2) + const group = await client1 + .conversations() + .createGroup([user2.account.address]) + + expect(group.groupPermissions().policySet()).toEqual({ + addMemberPolicy: 0, + removeMemberPolicy: 2, + addAdminPolicy: 3, + removeAdminPolicy: 3, + updateGroupNamePolicy: 0, + updateGroupDescriptionPolicy: 0, + updateGroupImageUrlSquarePolicy: 0, + updateGroupPinnedFrameUrlPolicy: 0, + }) + + await group.updatePermissionPolicy( + PermissionUpdateType.AddAdmin, + PermissionPolicy.Deny + ) + + expect(group.groupPermissions().policySet()).toEqual({ + addMemberPolicy: 0, + removeMemberPolicy: 2, + addAdminPolicy: 1, + removeAdminPolicy: 3, + updateGroupNamePolicy: 0, + updateGroupDescriptionPolicy: 0, + updateGroupImageUrlSquarePolicy: 0, + updateGroupPinnedFrameUrlPolicy: 0, + }) + + await group.updatePermissionPolicy( + PermissionUpdateType.UpdateMetadata, + PermissionPolicy.Deny, + MetadataField.GroupName + ) + + expect(group.groupPermissions().policySet()).toEqual({ + addMemberPolicy: 0, + removeMemberPolicy: 2, + addAdminPolicy: 1, + removeAdminPolicy: 3, + updateGroupNamePolicy: 1, + updateGroupDescriptionPolicy: 0, + updateGroupImageUrlSquarePolicy: 0, + updateGroupPinnedFrameUrlPolicy: 0, + }) + }) + it('should create a dm group', async () => { const user1 = createUser() const user2 = createUser() diff --git a/bindings_wasm/CHANGELOG.md b/bindings_wasm/CHANGELOG.md index 7755ac0c9..b55cfc295 100644 --- a/bindings_wasm/CHANGELOG.md +++ b/bindings_wasm/CHANGELOG.md @@ -1,5 +1,9 @@ # @xmtp/wasm-bindings +## 0.0.8 + +- Added support for custom permission policy sets + ## 0.0.7 - Moved `verify_signed_with_public_key` out of `Client` diff --git a/bindings_wasm/package.json b/bindings_wasm/package.json index 014358de6..82002185a 100644 --- a/bindings_wasm/package.json +++ b/bindings_wasm/package.json @@ -1,6 +1,6 @@ { "name": "@xmtp/wasm-bindings", - "version": "0.0.7", + "version": "0.0.8", "type": "module", "license": "MIT", "description": "WASM bindings for the libXMTP rust library", diff --git a/bindings_wasm/src/conversation.rs b/bindings_wasm/src/conversation.rs index 9c7a1e090..af09d83b2 100644 --- a/bindings_wasm/src/conversation.rs +++ b/bindings_wasm/src/conversation.rs @@ -6,10 +6,13 @@ use xmtp_mls::storage::group::ConversationType; use crate::client::RustXmtpClient; use crate::encoded_content::EncodedContent; use crate::messages::{ListMessagesOptions, Message}; +use crate::permissions::{MetadataField, PermissionPolicy, PermissionUpdateType}; use crate::{consent_state::ConsentState, permissions::GroupPermissions}; use xmtp_cryptography::signature::ed25519_public_key_to_address; use xmtp_mls::groups::{ group_metadata::GroupMetadata as XmtpGroupMetadata, + group_mutable_metadata::MetadataField as XmtpMetadataField, + intents::PermissionUpdateType as XmtpPermissionUpdateType, members::PermissionLevel as XmtpPermissionLevel, MlsGroup, UpdateAdminListType, }; use xmtp_mls::storage::group_message::{GroupMessageKind as XmtpGroupMessageKind, MsgQueryArgs}; @@ -543,4 +546,22 @@ impl Conversation { .dm_inbox_id() .map_err(|e| JsError::new(&format!("{e}"))) } + + #[wasm_bindgen(js_name = updatePermissionPolicy)] + pub async fn update_permission_policy( + &self, + permission_update_type: PermissionUpdateType, + permission_policy_option: PermissionPolicy, + metadata_field: Option, + ) -> Result<(), JsError> { + self + .to_mls_group() + .update_permission_policy( + XmtpPermissionUpdateType::from(&permission_update_type), + permission_policy_option.try_into()?, + metadata_field.map(|field| XmtpMetadataField::from(&field)), + ) + .await + .map_err(Into::into) + } } diff --git a/bindings_wasm/src/conversations.rs b/bindings_wasm/src/conversations.rs index 50f0790a2..599b95427 100644 --- a/bindings_wasm/src/conversations.rs +++ b/bindings_wasm/src/conversations.rs @@ -7,7 +7,7 @@ use xmtp_mls::storage::group::GroupMembershipState as XmtpGroupMembershipState; use xmtp_mls::storage::group::GroupQueryArgs; use crate::messages::Message; -use crate::permissions::GroupPermissionsOptions; +use crate::permissions::{GroupPermissionsOptions, PermissionPolicySet}; use crate::{client::RustXmtpClient, conversation::Conversation}; #[wasm_bindgen] @@ -127,6 +127,8 @@ pub struct CreateGroupOptions { pub group_description: Option, #[wasm_bindgen(js_name = groupPinnedFrameUrl)] pub group_pinned_frame_url: Option, + #[wasm_bindgen(js_name = customPermissionPolicySet)] + pub custom_permission_policy_set: Option, } #[wasm_bindgen] @@ -138,6 +140,7 @@ impl CreateGroupOptions { group_image_url_square: Option, group_description: Option, group_pinned_frame_url: Option, + custom_permission_policy_set: Option, ) -> Self { Self { permissions, @@ -145,6 +148,7 @@ impl CreateGroupOptions { group_image_url_square, group_description, group_pinned_frame_url, + custom_permission_policy_set, } } } @@ -187,9 +191,20 @@ impl Conversations { group_image_url_square: None, group_description: None, group_pinned_frame_url: None, + custom_permission_policy_set: None, }, }; + if let Some(GroupPermissionsOptions::CustomPolicy) = options.permissions { + if options.custom_permission_policy_set.is_none() { + return Err(JsError::new("CustomPolicy must include policy set")); + } + } else if options.custom_permission_policy_set.is_some() { + return Err(JsError::new("Only CustomPolicy may specify a policy set")); + } + + let metadata_options = options.clone().into_group_metadata_options(); + let group_permissions = match options.permissions { Some(GroupPermissionsOptions::AllMembers) => { Some(PreconfiguredPolicies::AllMembers.to_policy_set()) @@ -197,11 +212,20 @@ impl Conversations { Some(GroupPermissionsOptions::AdminOnly) => { Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()) } + Some(GroupPermissionsOptions::CustomPolicy) => { + if let Some(policy_set) = options.custom_permission_policy_set { + Some( + policy_set + .try_into() + .map_err(|e| JsError::new(format!("{}", e).as_str()))?, + ) + } else { + None + } + } _ => None, }; - let metadata_options = options.clone().into_group_metadata_options(); - let convo = if account_addresses.is_empty() { self .inner_client diff --git a/bindings_wasm/src/permissions.rs b/bindings_wasm/src/permissions.rs index 97ebc9f6d..78ce50221 100644 --- a/bindings_wasm/src/permissions.rs +++ b/bindings_wasm/src/permissions.rs @@ -1,9 +1,11 @@ +use std::collections::HashMap; use wasm_bindgen::{prelude::wasm_bindgen, JsError}; use xmtp_mls::groups::{ - group_mutable_metadata::MetadataField, + group_mutable_metadata::MetadataField as XmtpMetadataField, group_permissions::{ - BasePolicies, GroupMutablePermissions, MembershipPolicies, MetadataBasePolicies, - MetadataPolicies, PermissionsBasePolicies, PermissionsPolicies, + BasePolicies, GroupMutablePermissions, GroupMutablePermissionsError, MembershipPolicies, + MetadataBasePolicies, MetadataPolicies, PermissionsBasePolicies, PermissionsPolicies, + PolicySet, }, intents::{PermissionPolicyOption, PermissionUpdateType as XmtpPermissionUpdateType}, PreconfiguredPolicies, @@ -108,7 +110,49 @@ impl From<&PermissionsPolicies> for PermissionPolicy { } } +impl TryInto for PermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> Result { + match self { + PermissionPolicy::Allow => Ok(MetadataPolicies::allow()), + PermissionPolicy::Deny => Ok(MetadataPolicies::deny()), + PermissionPolicy::Admin => Ok(MetadataPolicies::allow_if_actor_admin()), + PermissionPolicy::SuperAdmin => Ok(MetadataPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl TryInto for PermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> Result { + match self { + PermissionPolicy::Deny => Ok(PermissionsPolicies::deny()), + PermissionPolicy::Admin => Ok(PermissionsPolicies::allow_if_actor_admin()), + PermissionPolicy::SuperAdmin => Ok(PermissionsPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + +impl TryInto for PermissionPolicy { + type Error = GroupMutablePermissionsError; + + fn try_into(self) -> Result { + match self { + PermissionPolicy::Allow => Ok(MembershipPolicies::allow()), + PermissionPolicy::Deny => Ok(MembershipPolicies::deny()), + PermissionPolicy::Admin => Ok(MembershipPolicies::allow_if_actor_admin()), + PermissionPolicy::SuperAdmin => Ok(MembershipPolicies::allow_if_actor_super_admin()), + _ => Err(GroupMutablePermissionsError::InvalidPermissionPolicyOption), + } + } +} + #[wasm_bindgen(getter_with_clone)] +#[derive(Clone)] pub struct PermissionPolicySet { #[wasm_bindgen(js_name = addMemberPolicy)] pub add_member_policy: PermissionPolicy, @@ -201,10 +245,65 @@ impl GroupPermissions { remove_member_policy: PermissionPolicy::from(&policy_set.remove_member_policy), add_admin_policy: PermissionPolicy::from(&policy_set.add_admin_policy), remove_admin_policy: PermissionPolicy::from(&policy_set.remove_admin_policy), - update_group_name_policy: get_policy(MetadataField::GroupName.as_str()), - update_group_description_policy: get_policy(MetadataField::Description.as_str()), - update_group_image_url_square_policy: get_policy(MetadataField::GroupImageUrlSquare.as_str()), - update_group_pinned_frame_url_policy: get_policy(MetadataField::GroupPinnedFrameUrl.as_str()), + update_group_name_policy: get_policy(XmtpMetadataField::GroupName.as_str()), + update_group_description_policy: get_policy(XmtpMetadataField::Description.as_str()), + update_group_image_url_square_policy: get_policy( + XmtpMetadataField::GroupImageUrlSquare.as_str(), + ), + update_group_pinned_frame_url_policy: get_policy( + XmtpMetadataField::GroupPinnedFrameUrl.as_str(), + ), + }) + } +} + +impl TryFrom for PolicySet { + type Error = GroupMutablePermissionsError; + fn try_from(policy_set: PermissionPolicySet) -> Result { + let mut metadata_permissions_map: HashMap = HashMap::new(); + metadata_permissions_map.insert( + XmtpMetadataField::GroupName.to_string(), + policy_set.update_group_name_policy.try_into()?, + ); + metadata_permissions_map.insert( + XmtpMetadataField::Description.to_string(), + policy_set.update_group_description_policy.try_into()?, + ); + metadata_permissions_map.insert( + XmtpMetadataField::GroupImageUrlSquare.to_string(), + policy_set.update_group_image_url_square_policy.try_into()?, + ); + metadata_permissions_map.insert( + XmtpMetadataField::GroupPinnedFrameUrl.to_string(), + policy_set.update_group_pinned_frame_url_policy.try_into()?, + ); + + Ok(PolicySet { + add_member_policy: policy_set.add_member_policy.try_into()?, + remove_member_policy: policy_set.remove_member_policy.try_into()?, + add_admin_policy: policy_set.add_admin_policy.try_into()?, + remove_admin_policy: policy_set.remove_admin_policy.try_into()?, + update_metadata_policy: metadata_permissions_map, + update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(), }) } } + +#[wasm_bindgen] +pub enum MetadataField { + GroupName, + Description, + ImageUrlSquare, + PinnedFrameUrl, +} + +impl From<&MetadataField> for XmtpMetadataField { + fn from(field: &MetadataField) -> Self { + match field { + MetadataField::GroupName => XmtpMetadataField::GroupName, + MetadataField::Description => XmtpMetadataField::Description, + MetadataField::ImageUrlSquare => XmtpMetadataField::GroupImageUrlSquare, + MetadataField::PinnedFrameUrl => XmtpMetadataField::GroupPinnedFrameUrl, + } + } +}