diff --git a/bindings_ffi/src/mls.rs b/bindings_ffi/src/mls.rs index 2146f4939..5deae3dac 100644 --- a/bindings_ffi/src/mls.rs +++ b/bindings_ffi/src/mls.rs @@ -637,7 +637,7 @@ pub struct FfiConversations { #[derive(uniffi::Enum, Clone, Debug)] pub enum FfiGroupPermissionsOptions { - AllMembers, + Default, AdminOnly, CustomPolicy, } @@ -787,12 +787,13 @@ pub struct FfiPermissionPolicySet { pub update_group_description_policy: FfiPermissionPolicy, pub update_group_image_url_square_policy: FfiPermissionPolicy, pub update_group_pinned_frame_url_policy: FfiPermissionPolicy, + pub update_message_expiration_ms_policy: FfiPermissionPolicy, } impl From for FfiGroupPermissionsOptions { fn from(policy: PreconfiguredPolicies) -> Self { match policy { - PreconfiguredPolicies::AllMembers => FfiGroupPermissionsOptions::AllMembers, + PreconfiguredPolicies::Default => FfiGroupPermissionsOptions::Default, PreconfiguredPolicies::AdminsOnly => FfiGroupPermissionsOptions::AdminOnly, } } @@ -818,6 +819,18 @@ impl TryFrom for PolicySet { MetadataField::GroupPinnedFrameUrl.to_string(), policy_set.update_group_pinned_frame_url_policy.try_into()?, ); + // MessageExpirationFromMillis follows the same policy as MessageExpirationMillis + metadata_permissions_map.insert( + MetadataField::MessageExpirationFromMillis.to_string(), + policy_set + .update_message_expiration_ms_policy + .clone() + .try_into()?, + ); + metadata_permissions_map.insert( + MetadataField::MessageExpirationMillis.to_string(), + policy_set.update_message_expiration_ms_policy.try_into()?, + ); Ok(PolicySet { add_member_policy: policy_set.add_member_policy.try_into()?, @@ -876,8 +889,8 @@ impl FfiConversations { let metadata_options = opts.clone().into_group_metadata_options(); let group_permissions = match opts.permissions { - Some(FfiGroupPermissionsOptions::AllMembers) => { - Some(xmtp_mls::groups::PreconfiguredPolicies::AllMembers.to_policy_set()) + Some(FfiGroupPermissionsOptions::Default) => { + Some(xmtp_mls::groups::PreconfiguredPolicies::Default.to_policy_set()) } Some(FfiGroupPermissionsOptions::AdminOnly) => { Some(xmtp_mls::groups::PreconfiguredPolicies::AdminsOnly.to_policy_set()) @@ -1369,6 +1382,8 @@ pub struct FfiCreateGroupOptions { pub group_description: Option, pub group_pinned_frame_url: Option, pub custom_permission_policy_set: Option, + pub message_expiration_from_ms: Option, + pub message_expiration_ms: Option, } impl FfiCreateGroupOptions { @@ -1378,6 +1393,8 @@ impl FfiCreateGroupOptions { image_url_square: self.group_image_url_square, description: self.group_description, pinned_frame_url: self.group_pinned_frame_url, + message_expiration_from_ms: self.message_expiration_from_ms, + message_expiration_ms: self.message_expiration_ms, } } } @@ -2127,6 +2144,9 @@ impl FfiGroupPermissions { update_group_pinned_frame_url_policy: get_policy( MetadataField::GroupPinnedFrameUrl.as_str(), ), + update_message_expiration_ms_policy: get_policy( + MetadataField::MessageExpirationMillis.as_str(), + ), }) } } @@ -2847,6 +2867,8 @@ mod tests { group_description: Some("group description".to_string()), group_pinned_frame_url: Some("pinned frame".to_string()), custom_permission_policy_set: None, + message_expiration_from_ms: None, + message_expiration_ms: None, }, ) .await @@ -4057,7 +4079,7 @@ mod tests { } #[tokio::test(flavor = "multi_thread", worker_threads = 5)] - async fn test_permissions_show_expected_values() { + async fn test_group_permissions_show_expected_values() { let alix = new_test_client().await; let bo = new_test_client().await; // Create admin_only group @@ -4086,12 +4108,74 @@ mod tests { update_group_description_policy: FfiPermissionPolicy::Admin, update_group_image_url_square_policy: FfiPermissionPolicy::Admin, update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, + }; + assert_eq!(alix_permission_policy_set, expected_permission_policy_set); + + // Create all_members group + let all_members_options = FfiCreateGroupOptions { + permissions: Some(FfiGroupPermissionsOptions::Default), + ..Default::default() + }; + let alix_group_all_members = alix + .conversations() + .create_group(vec![bo.account_address.clone()], all_members_options) + .await + .unwrap(); + + // Verify we can read the expected permissions + let alix_permission_policy_set = alix_group_all_members + .group_permissions() + .unwrap() + .policy_set() + .unwrap(); + let expected_permission_policy_set = FfiPermissionPolicySet { + add_member_policy: FfiPermissionPolicy::Allow, + remove_member_policy: FfiPermissionPolicy::Admin, + add_admin_policy: FfiPermissionPolicy::SuperAdmin, + remove_admin_policy: FfiPermissionPolicy::SuperAdmin, + update_group_name_policy: FfiPermissionPolicy::Allow, + update_group_description_policy: FfiPermissionPolicy::Allow, + update_group_image_url_square_policy: FfiPermissionPolicy::Allow, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Allow, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, + }; + assert_eq!(alix_permission_policy_set, expected_permission_policy_set); + } + + #[tokio::test(flavor = "multi_thread", worker_threads = 5)] + async fn test_dm_permissions_show_expected_values() { + let alix = new_test_client().await; + let bo = new_test_client().await; + + let alix_group_admin_only = alix + .conversations() + .create_dm(bo.account_address.clone()) + .await + .unwrap(); + + // Verify we can read the expected permissions + let alix_permission_policy_set = alix_group_admin_only + .group_permissions() + .unwrap() + .policy_set() + .unwrap(); + let expected_permission_policy_set = FfiPermissionPolicySet { + add_member_policy: FfiPermissionPolicy::Deny, + remove_member_policy: FfiPermissionPolicy::Deny, + add_admin_policy: FfiPermissionPolicy::Deny, + remove_admin_policy: FfiPermissionPolicy::Deny, + update_group_name_policy: FfiPermissionPolicy::Allow, + update_group_description_policy: FfiPermissionPolicy::Allow, + update_group_image_url_square_policy: FfiPermissionPolicy::Allow, + update_group_pinned_frame_url_policy: FfiPermissionPolicy::Allow, + update_message_expiration_ms_policy: FfiPermissionPolicy::Allow, }; assert_eq!(alix_permission_policy_set, expected_permission_policy_set); // Create all_members group let all_members_options = FfiCreateGroupOptions { - permissions: Some(FfiGroupPermissionsOptions::AllMembers), + permissions: Some(FfiGroupPermissionsOptions::Default), ..Default::default() }; let alix_group_all_members = alix @@ -4115,6 +4199,7 @@ mod tests { update_group_description_policy: FfiPermissionPolicy::Allow, update_group_image_url_square_policy: FfiPermissionPolicy::Allow, update_group_pinned_frame_url_policy: FfiPermissionPolicy::Allow, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, }; assert_eq!(alix_permission_policy_set, expected_permission_policy_set); } @@ -4148,6 +4233,7 @@ mod tests { update_group_description_policy: FfiPermissionPolicy::Admin, update_group_image_url_square_policy: FfiPermissionPolicy::Admin, update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, }; assert_eq!(alix_group_permissions, expected_permission_policy_set); @@ -4175,6 +4261,7 @@ mod tests { update_group_description_policy: FfiPermissionPolicy::Admin, update_group_image_url_square_policy: FfiPermissionPolicy::Allow, update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, }; assert_eq!(alix_group_permissions, new_expected_permission_policy_set); @@ -4228,6 +4315,7 @@ mod tests { update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, add_member_policy: FfiPermissionPolicy::Allow, remove_member_policy: FfiPermissionPolicy::Deny, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, }; let create_group_options = FfiCreateGroupOptions { @@ -4237,6 +4325,8 @@ mod tests { group_description: Some("A test group".to_string()), group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), custom_permission_policy_set: Some(custom_permissions), + message_expiration_from_ms: None, + message_expiration_ms: None, }; let alix_group = alix @@ -4275,6 +4365,10 @@ mod tests { group_permissions_policy_set.update_group_pinned_frame_url_policy, FfiPermissionPolicy::Admin ); + assert_eq!( + group_permissions_policy_set.update_message_expiration_ms_policy, + FfiPermissionPolicy::Admin + ); assert_eq!( group_permissions_policy_set.add_member_policy, FfiPermissionPolicy::Allow @@ -4338,6 +4432,7 @@ mod tests { update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, add_member_policy: FfiPermissionPolicy::Allow, remove_member_policy: FfiPermissionPolicy::Deny, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, }; let custom_permissions_valid = FfiPermissionPolicySet { @@ -4349,6 +4444,7 @@ mod tests { update_group_pinned_frame_url_policy: FfiPermissionPolicy::Admin, add_member_policy: FfiPermissionPolicy::Allow, remove_member_policy: FfiPermissionPolicy::Deny, + update_message_expiration_ms_policy: FfiPermissionPolicy::Admin, }; let create_group_options_invalid_1 = FfiCreateGroupOptions { @@ -4358,6 +4454,8 @@ mod tests { group_description: Some("A test group".to_string()), group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), custom_permission_policy_set: Some(custom_permissions_invalid_1), + message_expiration_from_ms: None, + message_expiration_ms: None, }; let results_1 = alix @@ -4371,12 +4469,14 @@ mod tests { assert!(results_1.is_err()); let create_group_options_invalid_2 = FfiCreateGroupOptions { - permissions: Some(FfiGroupPermissionsOptions::AllMembers), + permissions: Some(FfiGroupPermissionsOptions::Default), group_name: Some("Test Group".to_string()), group_image_url_square: Some("https://example.com/image.png".to_string()), group_description: Some("A test group".to_string()), group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), custom_permission_policy_set: Some(custom_permissions_valid.clone()), + message_expiration_from_ms: None, + message_expiration_ms: None, }; let results_2 = alix @@ -4396,6 +4496,8 @@ mod tests { group_description: Some("A test group".to_string()), group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), custom_permission_policy_set: Some(custom_permissions_valid.clone()), + message_expiration_from_ms: None, + message_expiration_ms: None, }; let results_3 = alix @@ -4415,6 +4517,8 @@ mod tests { group_description: Some("A test group".to_string()), group_pinned_frame_url: Some("https://example.com/frame.png".to_string()), custom_permission_policy_set: Some(custom_permissions_valid), + message_expiration_from_ms: None, + message_expiration_ms: None, }; let results_4 = alix diff --git a/bindings_node/src/conversations.rs b/bindings_node/src/conversations.rs index b9eda875d..b9be3580c 100644 --- a/bindings_node/src/conversations.rs +++ b/bindings_node/src/conversations.rs @@ -122,6 +122,8 @@ pub struct CreateGroupOptions { pub group_description: Option, pub group_pinned_frame_url: Option, pub custom_permission_policy_set: Option, + pub message_expiration_from_ms: Option, + pub message_expiration_ms: Option, } impl CreateGroupOptions { @@ -131,6 +133,8 @@ impl CreateGroupOptions { image_url_square: self.group_image_url_square, description: self.group_description, pinned_frame_url: self.group_pinned_frame_url, + message_expiration_from_ms: self.message_expiration_from_ms, + message_expiration_ms: self.message_expiration_ms, } } } @@ -152,17 +156,16 @@ impl Conversations { account_addresses: Vec, options: Option, ) -> Result { - let options = match options { - Some(options) => options, - None => CreateGroupOptions { - permissions: None, - group_name: None, - group_image_url_square: None, - group_description: None, - group_pinned_frame_url: None, - custom_permission_policy_set: None, - }, - }; + let options = options.unwrap_or(CreateGroupOptions { + permissions: None, + group_name: None, + group_image_url_square: None, + group_description: None, + group_pinned_frame_url: None, + custom_permission_policy_set: None, + message_expiration_from_ms: None, + message_expiration_ms: None, + }); if let Some(GroupPermissionsOptions::CustomPolicy) = options.permissions { if options.custom_permission_policy_set.is_none() { @@ -177,8 +180,8 @@ impl Conversations { let metadata_options = options.clone().into_group_metadata_options(); let group_permissions = match options.permissions { - Some(GroupPermissionsOptions::AllMembers) => { - Some(PreconfiguredPolicies::AllMembers.to_policy_set()) + Some(GroupPermissionsOptions::Default) => { + Some(PreconfiguredPolicies::Default.to_policy_set()) } Some(GroupPermissionsOptions::AdminOnly) => { Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()) diff --git a/bindings_node/src/permissions.rs b/bindings_node/src/permissions.rs index 3008c4b4e..1eb667f64 100644 --- a/bindings_node/src/permissions.rs +++ b/bindings_node/src/permissions.rs @@ -14,7 +14,7 @@ use xmtp_mls::groups::{ #[napi] pub enum GroupPermissionsOptions { - AllMembers, + Default, AdminOnly, CustomPolicy, } @@ -161,12 +161,13 @@ pub struct PermissionPolicySet { pub update_group_description_policy: PermissionPolicy, pub update_group_image_url_square_policy: PermissionPolicy, pub update_group_pinned_frame_url_policy: PermissionPolicy, + pub update_message_expiration_ms_policy: PermissionPolicy, } impl From for GroupPermissionsOptions { fn from(policy: PreconfiguredPolicies) -> Self { match policy { - PreconfiguredPolicies::AllMembers => GroupPermissionsOptions::AllMembers, + PreconfiguredPolicies::Default => GroupPermissionsOptions::Default, PreconfiguredPolicies::AdminsOnly => GroupPermissionsOptions::AdminOnly, } } @@ -215,6 +216,9 @@ impl GroupPermissions { update_group_pinned_frame_url_policy: get_policy( XmtpMetadataField::GroupPinnedFrameUrl.as_str(), ), + update_message_expiration_ms_policy: get_policy( + XmtpMetadataField::MessageExpirationMillis.as_str(), + ), }) } } @@ -241,6 +245,10 @@ impl TryFrom for PolicySet { XmtpMetadataField::GroupPinnedFrameUrl.to_string(), policy_set.update_group_pinned_frame_url_policy.try_into()?, ); + metadata_permissions_map.insert( + XmtpMetadataField::MessageExpirationMillis.to_string(), + policy_set.update_message_expiration_ms_policy.try_into()?, + ); Ok(PolicySet { add_member_policy: policy_set.add_member_policy.try_into()?, diff --git a/bindings_node/test/Conversations.test.ts b/bindings_node/test/Conversations.test.ts index f9d7beefd..eccf8b0c1 100644 --- a/bindings_node/test/Conversations.test.ts +++ b/bindings_node/test/Conversations.test.ts @@ -41,7 +41,7 @@ describe('Conversations', () => { expect(group.isActive()).toBe(true) expect(group.groupName()).toBe('') expect(group.groupPermissions().policyType()).toBe( - GroupPermissionsOptions.AllMembers + GroupPermissionsOptions.Default ) expect(group.groupPermissions().policySet()).toEqual({ addMemberPolicy: 0, @@ -52,6 +52,7 @@ describe('Conversations', () => { updateGroupDescriptionPolicy: 0, updateGroupImageUrlSquarePolicy: 0, updateGroupPinnedFrameUrlPolicy: 0, + updateMessageExpirationMsPolicy: 2, }) expect(group.addedByInboxId()).toBe(client1.inboxId()) expect((await group.findMessages()).length).toBe(1) @@ -103,6 +104,7 @@ describe('Conversations', () => { updateGroupDescriptionPolicy: 1, updateGroupImageUrlSquarePolicy: 0, updateGroupPinnedFrameUrlPolicy: 3, + updateMessageExpirationMsPolicy: 2, }, }) expect(group).toBeDefined() @@ -118,6 +120,7 @@ describe('Conversations', () => { updateGroupDescriptionPolicy: 1, updateGroupImageUrlSquarePolicy: 0, updateGroupPinnedFrameUrlPolicy: 3, + updateMessageExpirationMsPolicy: 2, }) }) @@ -139,6 +142,7 @@ describe('Conversations', () => { updateGroupDescriptionPolicy: 0, updateGroupImageUrlSquarePolicy: 0, updateGroupPinnedFrameUrlPolicy: 0, + updateMessageExpirationMsPolicy: 2, }) await group.updatePermissionPolicy( @@ -155,6 +159,7 @@ describe('Conversations', () => { updateGroupDescriptionPolicy: 0, updateGroupImageUrlSquarePolicy: 0, updateGroupPinnedFrameUrlPolicy: 0, + updateMessageExpirationMsPolicy: 2, }) await group.updatePermissionPolicy( @@ -172,6 +177,7 @@ describe('Conversations', () => { updateGroupDescriptionPolicy: 0, updateGroupImageUrlSquarePolicy: 0, updateGroupPinnedFrameUrlPolicy: 0, + updateMessageExpirationMsPolicy: 2, }) }) @@ -198,6 +204,7 @@ describe('Conversations', () => { updateGroupImageUrlSquarePolicy: 0, updateGroupNamePolicy: 0, updateGroupPinnedFrameUrlPolicy: 0, + updateMessageExpirationMsPolicy: 0, }) expect(group.addedByInboxId()).toBe(client1.inboxId()) expect((await group.findMessages()).length).toBe(0) @@ -335,6 +342,7 @@ describe('Conversations', () => { updateGroupDescriptionPolicy: 2, updateGroupImageUrlSquarePolicy: 2, updateGroupPinnedFrameUrlPolicy: 2, + updateMessageExpirationMsPolicy: 2, }) const groupWithDescription = await client1 diff --git a/bindings_wasm/src/conversations.rs b/bindings_wasm/src/conversations.rs index 50d907666..95863e2eb 100644 --- a/bindings_wasm/src/conversations.rs +++ b/bindings_wasm/src/conversations.rs @@ -129,11 +129,16 @@ pub struct CreateGroupOptions { pub group_pinned_frame_url: Option, #[wasm_bindgen(js_name = customPermissionPolicySet)] pub custom_permission_policy_set: Option, + #[wasm_bindgen(js_name = messageExpirationFromMillis)] + pub message_expiration_from_ms: Option, + #[wasm_bindgen(js_name = messageExpirationMillis)] + pub message_expiration_ms: Option, } #[wasm_bindgen] impl CreateGroupOptions { #[wasm_bindgen(constructor)] + #[allow(clippy::too_many_arguments)] pub fn new( permissions: Option, group_name: Option, @@ -141,6 +146,8 @@ impl CreateGroupOptions { group_description: Option, group_pinned_frame_url: Option, custom_permission_policy_set: Option, + message_expiration_from_ms: Option, + message_expiration_ms: Option, ) -> Self { Self { permissions, @@ -149,6 +156,8 @@ impl CreateGroupOptions { group_description, group_pinned_frame_url, custom_permission_policy_set, + message_expiration_from_ms, + message_expiration_ms, } } } @@ -160,6 +169,8 @@ impl CreateGroupOptions { image_url_square: self.group_image_url_square, description: self.group_description, pinned_frame_url: self.group_pinned_frame_url, + message_expiration_from_ms: self.message_expiration_from_ms, + message_expiration_ms: self.message_expiration_ms, } } } @@ -183,17 +194,16 @@ impl Conversations { account_addresses: Vec, options: Option, ) -> Result { - let options = match options { - Some(options) => options, - None => CreateGroupOptions { - permissions: None, - group_name: None, - group_image_url_square: None, - group_description: None, - group_pinned_frame_url: None, - custom_permission_policy_set: None, - }, - }; + let options = options.unwrap_or(CreateGroupOptions { + permissions: None, + group_name: None, + group_image_url_square: None, + group_description: None, + group_pinned_frame_url: None, + custom_permission_policy_set: None, + message_expiration_from_ms: None, + message_expiration_ms: None, + }); if let Some(GroupPermissionsOptions::CustomPolicy) = options.permissions { if options.custom_permission_policy_set.is_none() { @@ -206,8 +216,8 @@ impl Conversations { let metadata_options = options.clone().into_group_metadata_options(); let group_permissions = match options.permissions { - Some(GroupPermissionsOptions::AllMembers) => { - Some(PreconfiguredPolicies::AllMembers.to_policy_set()) + Some(GroupPermissionsOptions::Default) => { + Some(PreconfiguredPolicies::Default.to_policy_set()) } Some(GroupPermissionsOptions::AdminOnly) => { Some(PreconfiguredPolicies::AdminsOnly.to_policy_set()) diff --git a/bindings_wasm/src/permissions.rs b/bindings_wasm/src/permissions.rs index 78ce50221..2baa7ca74 100644 --- a/bindings_wasm/src/permissions.rs +++ b/bindings_wasm/src/permissions.rs @@ -14,7 +14,7 @@ use xmtp_mls::groups::{ #[wasm_bindgen] #[derive(Clone)] pub enum GroupPermissionsOptions { - AllMembers, + Default, AdminOnly, CustomPolicy, } @@ -170,6 +170,8 @@ pub struct PermissionPolicySet { pub update_group_image_url_square_policy: PermissionPolicy, #[wasm_bindgen(js_name = updateGroupPinnedFrameUrlPolicy)] pub update_group_pinned_frame_url_policy: PermissionPolicy, + #[wasm_bindgen(js_name = updateMessageExpirationPolicy)] + pub update_message_expiration_ms_policy: PermissionPolicy, } #[wasm_bindgen] @@ -185,6 +187,7 @@ impl PermissionPolicySet { update_group_description_policy: PermissionPolicy, update_group_image_url_square_policy: PermissionPolicy, update_group_pinned_frame_url_policy: PermissionPolicy, + update_message_expiration_ms_policy: PermissionPolicy, ) -> Self { Self { add_member_policy, @@ -195,6 +198,7 @@ impl PermissionPolicySet { update_group_description_policy, update_group_image_url_square_policy, update_group_pinned_frame_url_policy, + update_message_expiration_ms_policy, } } } @@ -202,7 +206,7 @@ impl PermissionPolicySet { impl From for GroupPermissionsOptions { fn from(policy: PreconfiguredPolicies) -> Self { match policy { - PreconfiguredPolicies::AllMembers => GroupPermissionsOptions::AllMembers, + PreconfiguredPolicies::Default => GroupPermissionsOptions::Default, PreconfiguredPolicies::AdminsOnly => GroupPermissionsOptions::AdminOnly, } } @@ -253,6 +257,9 @@ impl GroupPermissions { update_group_pinned_frame_url_policy: get_policy( XmtpMetadataField::GroupPinnedFrameUrl.as_str(), ), + update_message_expiration_ms_policy: get_policy( + XmtpMetadataField::MessageExpirationMillis.as_str(), + ), }) } } @@ -277,6 +284,10 @@ impl TryFrom for PolicySet { XmtpMetadataField::GroupPinnedFrameUrl.to_string(), policy_set.update_group_pinned_frame_url_policy.try_into()?, ); + metadata_permissions_map.insert( + XmtpMetadataField::MessageExpirationMillis.to_string(), + policy_set.update_message_expiration_ms_policy.try_into()?, + ); Ok(PolicySet { add_member_policy: policy_set.add_member_policy.try_into()?, diff --git a/examples/cli/cli-client.rs b/examples/cli/cli-client.rs index ee836e854..127883443 100755 --- a/examples/cli/cli-client.rs +++ b/examples/cli/cli-client.rs @@ -406,7 +406,7 @@ async fn main() -> color_eyre::eyre::Result<()> { } Commands::CreateGroup { permissions } => { let group_permissions = match permissions { - Permissions::EveryoneIsAdmin => xmtp_mls::groups::PreconfiguredPolicies::AllMembers, + Permissions::EveryoneIsAdmin => xmtp_mls::groups::PreconfiguredPolicies::Default, Permissions::GroupCreatorIsAdmin => { xmtp_mls::groups::PreconfiguredPolicies::AdminsOnly } diff --git a/xmtp_mls/src/groups/group_mutable_metadata.rs b/xmtp_mls/src/groups/group_mutable_metadata.rs index 2db12769e..e1285a2aa 100644 --- a/xmtp_mls/src/groups/group_mutable_metadata.rs +++ b/xmtp_mls/src/groups/group_mutable_metadata.rs @@ -47,6 +47,8 @@ pub enum MetadataField { Description, GroupImageUrlSquare, GroupPinnedFrameUrl, + MessageExpirationFromMillis, + MessageExpirationMillis, } impl MetadataField { @@ -57,6 +59,8 @@ impl MetadataField { MetadataField::Description => "description", MetadataField::GroupImageUrlSquare => "group_image_url_square", MetadataField::GroupPinnedFrameUrl => "group_pinned_frame_url", + MetadataField::MessageExpirationFromMillis => "message_expiration_from_ms", + MetadataField::MessageExpirationMillis => "message_expiration_ms", } } } @@ -121,6 +125,20 @@ impl GroupMutableMetadata { opts.pinned_frame_url .unwrap_or_else(|| DEFAULT_GROUP_PINNED_FRAME_URL.to_string()), ); + + if let Some(message_expiration_from_ms) = opts.message_expiration_from_ms { + attributes.insert( + MetadataField::MessageExpirationFromMillis.to_string(), + message_expiration_from_ms.to_string(), + ); + } + if let Some(message_expiration_ms) = opts.message_expiration_ms { + attributes.insert( + MetadataField::MessageExpirationMillis.to_string(), + message_expiration_ms.to_string(), + ); + } + let admin_list = vec![]; let super_admin_list = vec![creator_inbox_id.clone()]; Self { @@ -168,6 +186,7 @@ impl GroupMutableMetadata { MetadataField::Description, MetadataField::GroupImageUrlSquare, MetadataField::GroupPinnedFrameUrl, + MetadataField::MessageExpirationMillis, ] } diff --git a/xmtp_mls/src/groups/group_permissions.rs b/xmtp_mls/src/groups/group_permissions.rs index 1a1b04594..f15f4c6b6 100644 --- a/xmtp_mls/src/groups/group_permissions.rs +++ b/xmtp_mls/src/groups/group_permissions.rs @@ -25,12 +25,13 @@ use xmtp_proto::xmtp::mls::message_contents::{ PermissionsUpdatePolicy as PermissionsPolicyProto, PolicySet as PolicySetProto, }; -use crate::configuration::{GROUP_PERMISSIONS_EXTENSION_ID, SUPER_ADMIN_METADATA_PREFIX}; - use super::{ group_mutable_metadata::GroupMutableMetadata, validated_commit::{CommitParticipant, Inbox, MetadataFieldChange, ValidatedCommit}, }; +use crate::configuration::{GROUP_PERMISSIONS_EXTENSION_ID, SUPER_ADMIN_METADATA_PREFIX}; +use crate::groups::group_mutable_metadata::MetadataField; +use crate::groups::group_mutable_metadata::MetadataField::MessageExpirationMillis; /// Errors that can occur when working with GroupMutablePermissions. #[derive(Debug, Error)] @@ -227,7 +228,11 @@ impl MetadataPolicies { pub fn default_map(policies: MetadataPolicies) -> HashMap { let mut map: HashMap = HashMap::new(); for field in GroupMutableMetadata::supported_fields() { - map.insert(field.to_string(), policies.clone()); + if field == MessageExpirationMillis { + map.insert(field.to_string(), MetadataPolicies::allow_if_actor_admin()); + } else { + map.insert(field.to_string(), policies.clone()); + } } map } @@ -1145,7 +1150,7 @@ impl PolicySet { /// since the group was created, the number of metadata policies might not match /// the default All Members Policy Set. As long as all metadata policies are allow, we will /// match against All Members Preconfigured Policy -pub fn is_policy_all_members(policy: &PolicySet) -> Result { +pub fn is_policy_default(policy: &PolicySet) -> Result { let mut metadata_policies_equal = true; for field_name in policy.update_metadata_policy.keys() { let metadata_policy = policy.update_metadata_policy.get(field_name).ok_or( @@ -1153,8 +1158,13 @@ pub fn is_policy_all_members(policy: &PolicySet) -> Result { name: field_name.to_string(), }, )?; - metadata_policies_equal = - metadata_policies_equal && metadata_policy.eq(&MetadataPolicies::allow()); + if field_name == MessageExpirationMillis.as_str() { + metadata_policies_equal = metadata_policies_equal + && metadata_policy.eq(&MetadataPolicies::allow_if_actor_admin()); + } else { + metadata_policies_equal = + metadata_policies_equal && metadata_policy.eq(&MetadataPolicies::allow()); + } } Ok(metadata_policies_equal && policy.add_member_policy == MembershipPolicies::allow() @@ -1192,11 +1202,16 @@ pub fn is_policy_admin_only(policy: &PolicySet) -> Result { /// Returns the "All Members" preconfigured policy. /// /// A policy where any member can add or remove any other member -pub(crate) fn policy_all_members() -> PolicySet { +pub(crate) fn default_policy() -> PolicySet { let mut metadata_policies_map: HashMap = HashMap::new(); for field in GroupMutableMetadata::supported_fields() { metadata_policies_map.insert(field.to_string(), MetadataPolicies::allow()); } + metadata_policies_map.insert( + MessageExpirationMillis.to_string(), + MetadataPolicies::allow_if_actor_admin(), + ); + PolicySet::new( MembershipPolicies::allow(), MembershipPolicies::allow_if_actor_admin(), @@ -1215,6 +1230,11 @@ pub(crate) fn policy_admin_only() -> PolicySet { for field in GroupMutableMetadata::supported_fields() { metadata_policies_map.insert(field.to_string(), MetadataPolicies::allow_if_actor_admin()); } + metadata_policies_map.insert( + MetadataField::MessageExpirationMillis.to_string(), + MetadataPolicies::allow_if_actor_admin(), + ); + PolicySet::new( MembershipPolicies::allow_if_actor_admin(), MembershipPolicies::allow_if_actor_admin(), @@ -1237,7 +1257,7 @@ impl Default for PolicySet { pub enum PreconfiguredPolicies { /// The "All Members" preconfigured policy. #[default] - AllMembers, + Default, /// The "Admin Only" preconfigured policy. AdminsOnly, } @@ -1246,15 +1266,15 @@ impl PreconfiguredPolicies { /// Converts the PreconfiguredPolicies to a PolicySet. pub fn to_policy_set(&self) -> PolicySet { match self { - PreconfiguredPolicies::AllMembers => policy_all_members(), + PreconfiguredPolicies::Default => default_policy(), PreconfiguredPolicies::AdminsOnly => policy_admin_only(), } } /// Creates a PreconfiguredPolicies from a PolicySet. pub fn from_policy_set(policy_set: &PolicySet) -> Result { - if is_policy_all_members(policy_set)? { - Ok(PreconfiguredPolicies::AllMembers) + if is_policy_default(policy_set)? { + Ok(PreconfiguredPolicies::Default) } else if is_policy_admin_only(policy_set)? { Ok(PreconfiguredPolicies::AdminsOnly) } else { @@ -1614,11 +1634,11 @@ pub(crate) mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_preconfigured_policy() { - let group_permissions = GroupMutablePermissions::new(policy_all_members()); + let group_permissions = GroupMutablePermissions::new(default_policy()); assert_eq!( group_permissions.preconfigured_policy().unwrap(), - PreconfiguredPolicies::AllMembers + PreconfiguredPolicies::Default ); let group_group_permissions_creator_admin = @@ -1647,7 +1667,7 @@ pub(crate) mod tests { update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(), }; - assert!(is_policy_all_members(&policy_set_new_metadata_permission).unwrap()); + assert!(is_policy_default(&policy_set_new_metadata_permission).unwrap()); let mut metadata_policies_map = MetadataPolicies::default_map(MetadataPolicies::allow_if_actor_admin()); diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 0d4049d9a..d8c4487f8 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -293,6 +293,8 @@ pub struct GroupMetadataOptions { pub image_url_square: Option, pub description: Option, pub pinned_frame_url: Option, + pub message_expiration_from_ms: Option, + pub message_expiration_ms: Option, } impl Clone for MlsGroup { @@ -2591,6 +2593,8 @@ pub(crate) mod tests { image_url_square: Some("url".to_string()), description: Some("group description".to_string()), pinned_frame_url: Some("pinned frame".to_string()), + message_expiration_from_ms: None, + message_expiration_ms: None, }, ) .unwrap(); @@ -2821,7 +2825,7 @@ pub(crate) mod tests { let bola = ClientBuilder::new_test_client(&bola_wallet).await; // Create a group and verify it has the default group name - let policy_set = Some(PreconfiguredPolicies::AllMembers.to_policy_set()); + let policy_set = Some(PreconfiguredPolicies::Default.to_policy_set()); let amal_group = amal .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); @@ -3275,7 +3279,7 @@ pub(crate) mod tests { #[wasm_bindgen_test(unsupported = tokio::test(flavor = "current_thread"))] async fn test_can_read_group_creator_inbox_id() { let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let policy_set = Some(PreconfiguredPolicies::AllMembers.to_policy_set()); + let policy_set = Some(PreconfiguredPolicies::Default.to_policy_set()); let amal_group = amal .create_group(policy_set, GroupMetadataOptions::default()) .unwrap(); @@ -3303,7 +3307,7 @@ pub(crate) mod tests { async fn test_can_update_gce_after_failed_commit() { // Step 1: Amal creates a group let amal = ClientBuilder::new_test_client(&generate_local_wallet()).await; - let policy_set = Some(PreconfiguredPolicies::AllMembers.to_policy_set()); + let policy_set = Some(PreconfiguredPolicies::Default.to_policy_set()); let amal_group = amal .create_group(policy_set, GroupMetadataOptions::default()) .unwrap();