1use openmls::{
2 extensions::{Extension, Extensions, UnknownExtension},
3 group::{GroupContext, MlsGroup as OpenMlsGroup},
4};
5use prost::Message;
6use std::collections::HashMap;
7use thiserror::Error;
8use xmtp_common::ErrorCode;
9use xmtp_proto::xmtp::mls::message_contents::{
10 GroupMutablePermissionsV1 as GroupMutablePermissionsProto,
11 MembershipPolicy as MembershipPolicyProto, MetadataPolicy as MetadataPolicyProto,
12 PermissionsUpdatePolicy as PermissionsPolicyProto, PolicySet as PolicySetProto,
13 membership_policy::{
14 AndCondition as AndConditionProto, AnyCondition as AnyConditionProto,
15 BasePolicy as BasePolicyProto, Kind as PolicyKindProto,
16 },
17 metadata_policy::{
18 AndCondition as MetadataAndConditionProto, AnyCondition as MetadataAnyConditionProto,
19 Kind as MetadataPolicyKindProto, MetadataBasePolicy as MetadataBasePolicyProto,
20 },
21 permissions_update_policy::{
22 AndCondition as PermissionsAndConditionProto, AnyCondition as PermissionsAnyConditionProto,
23 Kind as PermissionsPolicyKindProto, PermissionsBasePolicy as PermissionsBasePolicyProto,
24 },
25};
26
27use super::validated_commit::{CommitParticipant, Inbox, MetadataFieldChange, ValidatedCommit};
28use xmtp_configuration::{GROUP_PERMISSIONS_EXTENSION_ID, SUPER_ADMIN_METADATA_PREFIX};
29use xmtp_mls_common::group_mutable_metadata::{GroupMutableMetadata, MetadataField};
30
31#[derive(Debug, Error, ErrorCode)]
33pub enum GroupMutablePermissionsError {
34 #[error("serialization: {0}")]
38 Serialization(#[from] prost::EncodeError),
39 #[error("deserialization: {0}")]
43 Deserialization(#[from] prost::DecodeError),
44 #[error("policy error {0}")]
48 Policy(#[from] PolicyError),
49 #[error("invalid conversation type")]
53 InvalidConversationType,
54 #[error("missing policies")]
58 MissingPolicies,
59 #[error("missing extension")]
63 MissingExtension,
64 #[error("invalid permission policy option")]
68 InvalidPermissionPolicyOption,
69}
70
71#[derive(Debug, Clone, PartialEq)]
75pub struct GroupMutablePermissions {
76 pub policies: PolicySet,
78}
79
80impl GroupMutablePermissions {
81 pub fn new(policies: PolicySet) -> Self {
83 Self { policies }
84 }
85
86 pub fn preconfigured_policy(
88 &self,
89 ) -> Result<PreconfiguredPolicies, GroupMutablePermissionsError> {
90 Ok(PreconfiguredPolicies::from_policy_set(&self.policies)?)
91 }
92
93 pub(crate) fn from_proto(
95 proto: GroupMutablePermissionsProto,
96 ) -> Result<Self, GroupMutablePermissionsError> {
97 if proto.policies.is_none() {
98 return Err(GroupMutablePermissionsError::MissingPolicies);
99 }
100 let policies = proto.policies.expect("checked for none");
101
102 Ok(Self::new(PolicySet::from_proto(policies)?))
103 }
104
105 pub(crate) fn to_proto(
107 &self,
108 ) -> Result<GroupMutablePermissionsProto, GroupMutablePermissionsError> {
109 Ok(GroupMutablePermissionsProto {
110 policies: Some(self.policies.to_proto()?),
111 })
112 }
113}
114
115impl TryFrom<GroupMutablePermissions> for Vec<u8> {
117 type Error = GroupMutablePermissionsError;
118
119 fn try_from(value: GroupMutablePermissions) -> Result<Self, Self::Error> {
120 let mut buf = Vec::new();
121 let proto_val = value.to_proto()?;
122 proto_val.encode(&mut buf)?;
123
124 Ok(buf)
125 }
126}
127
128impl TryFrom<&Vec<u8>> for GroupMutablePermissions {
130 type Error = GroupMutablePermissionsError;
131
132 fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
133 let proto_val = GroupMutablePermissionsProto::decode(value.as_slice())?;
134 Self::from_proto(proto_val)
135 }
136}
137
138impl TryFrom<GroupMutablePermissionsProto> for GroupMutablePermissions {
140 type Error = GroupMutablePermissionsError;
141
142 fn try_from(value: GroupMutablePermissionsProto) -> Result<Self, Self::Error> {
143 Self::from_proto(value)
144 }
145}
146
147impl TryFrom<&Extensions<GroupContext>> for GroupMutablePermissions {
149 type Error = GroupMutablePermissionsError;
150
151 fn try_from(value: &Extensions<GroupContext>) -> Result<Self, Self::Error> {
152 for extension in value.iter() {
153 if let Extension::Unknown(GROUP_PERMISSIONS_EXTENSION_ID, UnknownExtension(metadata)) =
154 extension
155 {
156 return GroupMutablePermissions::try_from(metadata);
157 }
158 }
159 Err(GroupMutablePermissionsError::MissingExtension)
160 }
161}
162
163impl TryFrom<&OpenMlsGroup> for GroupMutablePermissions {
165 type Error = GroupMutablePermissionsError;
166
167 fn try_from(value: &OpenMlsGroup) -> Result<Self, Self::Error> {
168 let extensions = value.extensions();
169 extensions.try_into()
170 }
171}
172
173pub fn extract_group_permissions(
175 group: &OpenMlsGroup,
176) -> Result<GroupMutablePermissions, GroupMutablePermissionsError> {
177 let extensions = group.extensions();
178 extensions.try_into()
179}
180
181pub trait MetadataPolicy: std::fmt::Debug {
183 fn evaluate(&self, actor: &CommitParticipant, change: &MetadataFieldChange) -> bool;
188
189 fn to_proto(&self) -> Result<MetadataPolicyProto, PolicyError>;
191}
192
193#[derive(Clone, Copy, Debug, PartialEq)]
195pub enum MetadataBasePolicies {
196 Allow,
197 Deny,
198 AllowIfActorAdminOrSuperAdmin,
199 AllowIfActorSuperAdmin,
200}
201
202impl MetadataPolicy for &MetadataBasePolicies {
204 fn evaluate(&self, actor: &CommitParticipant, _change: &MetadataFieldChange) -> bool {
205 match self {
206 MetadataBasePolicies::Allow => true,
207 MetadataBasePolicies::Deny => false,
208 MetadataBasePolicies::AllowIfActorAdminOrSuperAdmin => {
209 actor.is_admin || actor.is_super_admin
210 }
211 MetadataBasePolicies::AllowIfActorSuperAdmin => actor.is_super_admin,
212 }
213 }
214
215 fn to_proto(&self) -> Result<MetadataPolicyProto, PolicyError> {
216 let inner = match self {
217 MetadataBasePolicies::Allow => MetadataBasePolicyProto::Allow as i32,
218 MetadataBasePolicies::Deny => MetadataBasePolicyProto::Deny as i32,
219 MetadataBasePolicies::AllowIfActorAdminOrSuperAdmin => {
220 MetadataBasePolicyProto::AllowIfAdmin as i32
221 }
222 MetadataBasePolicies::AllowIfActorSuperAdmin => {
223 MetadataBasePolicyProto::AllowIfSuperAdmin as i32
224 }
225 };
226
227 Ok(MetadataPolicyProto {
228 kind: Some(MetadataPolicyKindProto::Base(inner)),
229 })
230 }
231}
232
233#[derive(Debug, Clone, PartialEq)]
235#[allow(dead_code)]
236pub enum MetadataPolicies {
237 Standard(MetadataBasePolicies),
238 AndCondition(MetadataAndCondition),
239 AnyCondition(MetadataAnyCondition),
240}
241
242impl MetadataPolicies {
243 pub fn default_map(policies: MetadataPolicies) -> HashMap<String, MetadataPolicies> {
245 let mut map: HashMap<String, MetadataPolicies> = HashMap::new();
246 for field in GroupMutableMetadata::supported_fields() {
247 match field {
248 MetadataField::MessageDisappearInNS => {
249 map.insert(field.to_string(), MetadataPolicies::allow_if_actor_admin());
250 }
251 MetadataField::MessageDisappearFromNS => {
252 map.insert(field.to_string(), MetadataPolicies::allow_if_actor_admin());
253 }
254 MetadataField::MinimumSupportedProtocolVersion => {
255 map.insert(
256 field.to_string(),
257 MetadataPolicies::allow_if_actor_super_admin(),
258 );
259 }
260 _ => {
261 map.insert(field.to_string(), policies.clone());
262 }
263 }
264 }
265 map
266 }
267
268 pub fn dm_map() -> HashMap<String, MetadataPolicies> {
270 let mut map: HashMap<String, MetadataPolicies> = HashMap::new();
271 for field in GroupMutableMetadata::supported_fields() {
272 map.insert(field.to_string(), MetadataPolicies::allow());
273 }
274 map
275 }
276
277 pub fn allow() -> Self {
279 MetadataPolicies::Standard(MetadataBasePolicies::Allow)
280 }
281
282 pub fn deny() -> Self {
284 MetadataPolicies::Standard(MetadataBasePolicies::Deny)
285 }
286
287 pub fn allow_if_actor_admin() -> Self {
289 MetadataPolicies::Standard(MetadataBasePolicies::AllowIfActorAdminOrSuperAdmin)
290 }
291
292 pub fn allow_if_actor_super_admin() -> Self {
294 MetadataPolicies::Standard(MetadataBasePolicies::AllowIfActorSuperAdmin)
295 }
296
297 pub fn and(policies: Vec<MetadataPolicies>) -> Self {
299 MetadataPolicies::AndCondition(MetadataAndCondition::new(policies))
300 }
301
302 pub fn any(policies: Vec<MetadataPolicies>) -> Self {
304 MetadataPolicies::AnyCondition(MetadataAnyCondition::new(policies))
305 }
306}
307
308impl TryFrom<MetadataPolicyProto> for MetadataPolicies {
310 type Error = PolicyError;
311
312 fn try_from(proto: MetadataPolicyProto) -> Result<Self, Self::Error> {
313 match proto.kind {
314 Some(MetadataPolicyKindProto::Base(inner)) => match inner {
315 1 => Ok(MetadataPolicies::allow()),
316 2 => Ok(MetadataPolicies::deny()),
317 3 => Ok(MetadataPolicies::allow_if_actor_admin()),
318 4 => Ok(MetadataPolicies::allow_if_actor_super_admin()),
319 _ => Err(PolicyError::InvalidMetadataPolicy),
320 },
321 Some(MetadataPolicyKindProto::AndCondition(inner)) => {
322 if inner.policies.is_empty() {
323 return Err(PolicyError::InvalidMetadataPolicy);
324 }
325 let policies = inner
326 .policies
327 .into_iter()
328 .map(|policy| policy.try_into())
329 .collect::<Result<Vec<MetadataPolicies>, PolicyError>>()?;
330
331 Ok(MetadataPolicies::and(policies))
332 }
333 Some(MetadataPolicyKindProto::AnyCondition(inner)) => {
334 if inner.policies.is_empty() {
335 return Err(PolicyError::InvalidMetadataPolicy);
336 }
337
338 let policies = inner
339 .policies
340 .into_iter()
341 .map(|policy| policy.try_into())
342 .collect::<Result<Vec<MetadataPolicies>, PolicyError>>()?;
343
344 Ok(MetadataPolicies::any(policies))
345 }
346 None => Err(PolicyError::InvalidMetadataPolicy),
347 }
348 }
349}
350
351impl MetadataPolicy for MetadataPolicies {
353 fn evaluate(&self, actor: &CommitParticipant, change: &MetadataFieldChange) -> bool {
354 match self {
355 MetadataPolicies::Standard(policy) => policy.evaluate(actor, change),
356 MetadataPolicies::AndCondition(policy) => policy.evaluate(actor, change),
357 MetadataPolicies::AnyCondition(policy) => policy.evaluate(actor, change),
358 }
359 }
360
361 fn to_proto(&self) -> Result<MetadataPolicyProto, PolicyError> {
362 Ok(match self {
363 MetadataPolicies::Standard(policy) => policy.to_proto()?,
364 MetadataPolicies::AndCondition(policy) => policy.to_proto()?,
365 MetadataPolicies::AnyCondition(policy) => policy.to_proto()?,
366 })
367 }
368}
369
370#[derive(Clone, Debug, PartialEq)]
372pub struct MetadataAndCondition {
373 policies: Vec<MetadataPolicies>,
374}
375
376impl MetadataAndCondition {
377 pub(super) fn new(policies: Vec<MetadataPolicies>) -> Self {
378 Self { policies }
379 }
380}
381
382impl MetadataPolicy for MetadataAndCondition {
384 fn evaluate(&self, actor: &CommitParticipant, change: &MetadataFieldChange) -> bool {
385 self.policies
386 .iter()
387 .all(|policy| policy.evaluate(actor, change))
388 }
389
390 fn to_proto(&self) -> Result<MetadataPolicyProto, PolicyError> {
391 Ok(MetadataPolicyProto {
392 kind: Some(MetadataPolicyKindProto::AndCondition(
393 MetadataAndConditionProto {
394 policies: self
395 .policies
396 .iter()
397 .map(|policy| policy.to_proto())
398 .collect::<Result<Vec<MetadataPolicyProto>, PolicyError>>()?,
399 },
400 )),
401 })
402 }
403}
404
405#[derive(Clone, Debug, PartialEq)]
407pub struct MetadataAnyCondition {
408 policies: Vec<MetadataPolicies>,
409}
410
411#[allow(dead_code)]
412impl MetadataAnyCondition {
413 pub(super) fn new(policies: Vec<MetadataPolicies>) -> Self {
414 Self { policies }
415 }
416}
417
418impl MetadataPolicy for MetadataAnyCondition {
420 fn evaluate(&self, actor: &CommitParticipant, change: &MetadataFieldChange) -> bool {
421 self.policies
422 .iter()
423 .any(|policy| policy.evaluate(actor, change))
424 }
425
426 fn to_proto(&self) -> Result<MetadataPolicyProto, PolicyError> {
427 Ok(MetadataPolicyProto {
428 kind: Some(MetadataPolicyKindProto::AnyCondition(
429 MetadataAnyConditionProto {
430 policies: self
431 .policies
432 .iter()
433 .map(|policy| policy.to_proto())
434 .collect::<Result<Vec<MetadataPolicyProto>, PolicyError>>()?,
435 },
436 )),
437 })
438 }
439}
440
441pub trait PermissionsPolicy: std::fmt::Debug {
443 fn evaluate(&self, actor: &CommitParticipant) -> bool;
445
446 fn to_proto(&self) -> Result<PermissionsPolicyProto, PolicyError>;
448}
449
450#[derive(Clone, Copy, Debug, PartialEq)]
452pub enum PermissionsBasePolicies {
453 Deny,
454 AllowIfActorAdminOrSuperAdmin,
455 AllowIfActorSuperAdmin,
456}
457
458impl PermissionsPolicy for &PermissionsBasePolicies {
460 fn evaluate(&self, actor: &CommitParticipant) -> bool {
461 match self {
462 PermissionsBasePolicies::Deny => false,
463 PermissionsBasePolicies::AllowIfActorAdminOrSuperAdmin => {
464 actor.is_admin || actor.is_super_admin
465 }
466 PermissionsBasePolicies::AllowIfActorSuperAdmin => actor.is_super_admin,
467 }
468 }
469
470 fn to_proto(&self) -> Result<PermissionsPolicyProto, PolicyError> {
471 let inner = match self {
472 PermissionsBasePolicies::Deny => PermissionsBasePolicyProto::Deny as i32,
473 PermissionsBasePolicies::AllowIfActorAdminOrSuperAdmin => {
474 PermissionsBasePolicyProto::AllowIfAdmin as i32
475 }
476 PermissionsBasePolicies::AllowIfActorSuperAdmin => {
477 PermissionsBasePolicyProto::AllowIfSuperAdmin as i32
478 }
479 };
480
481 Ok(PermissionsPolicyProto {
482 kind: Some(PermissionsPolicyKindProto::Base(inner)),
483 })
484 }
485}
486
487#[derive(Debug, Clone, PartialEq)]
489#[allow(dead_code)]
490pub enum PermissionsPolicies {
491 Standard(PermissionsBasePolicies),
492 AndCondition(PermissionsAndCondition),
493 AnyCondition(PermissionsAnyCondition),
494}
495
496impl PermissionsPolicies {
497 pub fn deny() -> Self {
499 PermissionsPolicies::Standard(PermissionsBasePolicies::Deny)
500 }
501
502 pub fn allow_if_actor_admin() -> Self {
504 PermissionsPolicies::Standard(PermissionsBasePolicies::AllowIfActorAdminOrSuperAdmin)
505 }
506
507 pub fn allow_if_actor_super_admin() -> Self {
509 PermissionsPolicies::Standard(PermissionsBasePolicies::AllowIfActorSuperAdmin)
510 }
511
512 pub fn and(policies: Vec<PermissionsPolicies>) -> Self {
514 PermissionsPolicies::AndCondition(PermissionsAndCondition::new(policies))
515 }
516
517 pub fn any(policies: Vec<PermissionsPolicies>) -> Self {
519 PermissionsPolicies::AnyCondition(PermissionsAnyCondition::new(policies))
520 }
521}
522
523impl TryFrom<PermissionsPolicyProto> for PermissionsPolicies {
525 type Error = PolicyError;
526
527 fn try_from(proto: PermissionsPolicyProto) -> Result<Self, Self::Error> {
528 match proto.kind {
529 Some(PermissionsPolicyKindProto::Base(inner)) => match inner {
530 1 => Ok(PermissionsPolicies::deny()),
531 2 => Ok(PermissionsPolicies::allow_if_actor_admin()),
532 3 => Ok(PermissionsPolicies::allow_if_actor_super_admin()),
533 _ => Err(PolicyError::InvalidPermissionsPolicy),
534 },
535 Some(PermissionsPolicyKindProto::AndCondition(inner)) => {
536 if inner.policies.is_empty() {
537 return Err(PolicyError::InvalidPermissionsPolicy);
538 }
539 let policies = inner
540 .policies
541 .into_iter()
542 .map(|policy| policy.try_into())
543 .collect::<Result<Vec<PermissionsPolicies>, PolicyError>>()?;
544
545 Ok(PermissionsPolicies::and(policies))
546 }
547 Some(PermissionsPolicyKindProto::AnyCondition(inner)) => {
548 if inner.policies.is_empty() {
549 return Err(PolicyError::InvalidPermissionsPolicy);
550 }
551
552 let policies = inner
553 .policies
554 .into_iter()
555 .map(|policy| policy.try_into())
556 .collect::<Result<Vec<PermissionsPolicies>, PolicyError>>()?;
557
558 Ok(PermissionsPolicies::any(policies))
559 }
560 None => Err(PolicyError::InvalidPermissionsPolicy),
561 }
562 }
563}
564
565impl PermissionsPolicy for PermissionsPolicies {
567 fn evaluate(&self, actor: &CommitParticipant) -> bool {
568 match self {
569 PermissionsPolicies::Standard(policy) => policy.evaluate(actor),
570 PermissionsPolicies::AndCondition(policy) => policy.evaluate(actor),
571 PermissionsPolicies::AnyCondition(policy) => policy.evaluate(actor),
572 }
573 }
574
575 fn to_proto(&self) -> Result<PermissionsPolicyProto, PolicyError> {
576 Ok(match self {
577 PermissionsPolicies::Standard(policy) => policy.to_proto()?,
578 PermissionsPolicies::AndCondition(policy) => policy.to_proto()?,
579 PermissionsPolicies::AnyCondition(policy) => policy.to_proto()?,
580 })
581 }
582}
583
584#[derive(Clone, Debug, PartialEq)]
586pub struct PermissionsAndCondition {
587 policies: Vec<PermissionsPolicies>,
588}
589
590impl PermissionsAndCondition {
591 pub(super) fn new(policies: Vec<PermissionsPolicies>) -> Self {
592 Self { policies }
593 }
594}
595
596impl PermissionsPolicy for PermissionsAndCondition {
598 fn evaluate(&self, actor: &CommitParticipant) -> bool {
599 self.policies.iter().all(|policy| policy.evaluate(actor))
600 }
601
602 fn to_proto(&self) -> Result<PermissionsPolicyProto, PolicyError> {
603 Ok(PermissionsPolicyProto {
604 kind: Some(PermissionsPolicyKindProto::AndCondition(
605 PermissionsAndConditionProto {
606 policies: self
607 .policies
608 .iter()
609 .map(|policy| policy.to_proto())
610 .collect::<Result<Vec<PermissionsPolicyProto>, PolicyError>>()?,
611 },
612 )),
613 })
614 }
615}
616
617#[derive(Clone, Debug, PartialEq)]
619pub struct PermissionsAnyCondition {
620 policies: Vec<PermissionsPolicies>,
621}
622
623#[allow(dead_code)]
624impl PermissionsAnyCondition {
625 pub(super) fn new(policies: Vec<PermissionsPolicies>) -> Self {
626 Self { policies }
627 }
628}
629
630impl PermissionsPolicy for PermissionsAnyCondition {
632 fn evaluate(&self, actor: &CommitParticipant) -> bool {
633 self.policies.iter().any(|policy| policy.evaluate(actor))
634 }
635
636 fn to_proto(&self) -> Result<PermissionsPolicyProto, PolicyError> {
637 Ok(PermissionsPolicyProto {
638 kind: Some(PermissionsPolicyKindProto::AnyCondition(
639 PermissionsAnyConditionProto {
640 policies: self
641 .policies
642 .iter()
643 .map(|policy| policy.to_proto())
644 .collect::<Result<Vec<PermissionsPolicyProto>, PolicyError>>()?,
645 },
646 )),
647 })
648 }
649}
650
651pub trait MembershipPolicy: std::fmt::Debug {
653 fn evaluate(&self, actor: &CommitParticipant, change: &Inbox) -> bool;
655
656 fn to_proto(&self) -> Result<MembershipPolicyProto, PolicyError>;
658}
659
660#[derive(Debug, Error)]
662pub enum PolicyError {
663 #[error("serialization {0}")]
664 Serialization(#[from] prost::EncodeError),
665 #[error("deserialization {0}")]
666 Deserialization(#[from] prost::DecodeError),
667 #[error("Missing metadata policy field: {name}")]
668 MissingMetadataPolicyField { name: String },
669 #[error("invalid policy")]
670 InvalidPolicy,
671 #[error("unexpected preset policy")]
672 InvalidPresetPolicy,
673 #[error("invalid metadata policy")]
674 InvalidMetadataPolicy,
675 #[error("invalid membership policy")]
676 InvalidMembershipPolicy,
677 #[error("invalid permissions policy")]
678 InvalidPermissionsPolicy,
679 #[error("from proto add member invalid policy")]
680 FromProtoAddMemberInvalidPolicy,
681 #[error("from proto remove member invalid policy")]
682 FromProtoRemoveMemberInvalidPolicy,
683 #[error("from proto add admin invalid policy")]
684 FromProtoAddAdminInvalidPolicy,
685 #[error("from proto remove admin invalid policy")]
686 FromProtoRemoveAdminInvalidPolicy,
687 #[error("from proto update permissions invalid policy")]
688 FromProtoUpdatePermissionsInvalidPolicy,
689}
690
691#[derive(Debug, Clone, Copy, PartialEq)]
693#[allow(dead_code)]
694#[repr(u8)]
695pub enum BasePolicies {
696 Allow,
697 Deny,
698 AllowSameMember,
700 AllowIfAdminOrSuperAdmin,
701 AllowIfSuperAdmin,
702}
703
704impl MembershipPolicy for BasePolicies {
706 fn evaluate(&self, actor: &CommitParticipant, inbox: &Inbox) -> bool {
707 match self {
708 BasePolicies::Allow => true,
709 BasePolicies::Deny => false,
710 BasePolicies::AllowSameMember => inbox.inbox_id == actor.inbox_id,
711 BasePolicies::AllowIfAdminOrSuperAdmin => actor.is_admin || actor.is_super_admin,
712 BasePolicies::AllowIfSuperAdmin => actor.is_super_admin,
713 }
714 }
715
716 fn to_proto(&self) -> Result<MembershipPolicyProto, PolicyError> {
717 let inner = match self {
718 BasePolicies::Allow => BasePolicyProto::Allow as i32,
719 BasePolicies::Deny => BasePolicyProto::Deny as i32,
720 BasePolicies::AllowSameMember => return Err(PolicyError::InvalidPolicy), BasePolicies::AllowIfAdminOrSuperAdmin => {
722 BasePolicyProto::AllowIfAdminOrSuperAdmin as i32
723 }
724 BasePolicies::AllowIfSuperAdmin => BasePolicyProto::AllowIfSuperAdmin as i32,
725 };
726
727 Ok(MembershipPolicyProto {
728 kind: Some(PolicyKindProto::Base(inner)),
729 })
730 }
731}
732
733#[derive(Debug, Clone, PartialEq)]
735#[allow(dead_code)]
736pub enum MembershipPolicies {
737 Standard(BasePolicies),
738 AndCondition(AndCondition),
739 AnyCondition(AnyCondition),
740}
741
742impl MembershipPolicies {
743 pub fn allow() -> Self {
745 MembershipPolicies::Standard(BasePolicies::Allow)
746 }
747
748 pub fn deny() -> Self {
750 MembershipPolicies::Standard(BasePolicies::Deny)
751 }
752
753 #[allow(dead_code)]
755 pub fn allow_if_actor_admin() -> Self {
756 MembershipPolicies::Standard(BasePolicies::AllowIfAdminOrSuperAdmin)
757 }
758
759 #[allow(dead_code)]
761 pub fn allow_if_actor_super_admin() -> Self {
762 MembershipPolicies::Standard(BasePolicies::AllowIfSuperAdmin)
763 }
764
765 pub fn and(policies: Vec<MembershipPolicies>) -> Self {
767 MembershipPolicies::AndCondition(AndCondition::new(policies))
768 }
769
770 pub fn any(policies: Vec<MembershipPolicies>) -> Self {
772 MembershipPolicies::AnyCondition(AnyCondition::new(policies))
773 }
774}
775
776impl TryFrom<MembershipPolicyProto> for MembershipPolicies {
778 type Error = PolicyError;
779
780 fn try_from(proto: MembershipPolicyProto) -> Result<Self, Self::Error> {
781 match proto.kind {
782 Some(PolicyKindProto::Base(inner)) => match inner {
783 1 => Ok(MembershipPolicies::allow()),
784 2 => Ok(MembershipPolicies::deny()),
785 3 => Ok(MembershipPolicies::allow_if_actor_admin()),
786 4 => Ok(MembershipPolicies::allow_if_actor_super_admin()),
787 _ => Err(PolicyError::InvalidMembershipPolicy),
788 },
789 Some(PolicyKindProto::AndCondition(inner)) => {
790 if inner.policies.is_empty() {
791 return Err(PolicyError::InvalidMembershipPolicy);
792 }
793 let policies = inner
794 .policies
795 .into_iter()
796 .map(|policy| policy.try_into())
797 .collect::<Result<Vec<MembershipPolicies>, PolicyError>>()?;
798
799 Ok(MembershipPolicies::and(policies))
800 }
801 Some(PolicyKindProto::AnyCondition(inner)) => {
802 if inner.policies.is_empty() {
803 return Err(PolicyError::InvalidMembershipPolicy);
804 }
805
806 let policies = inner
807 .policies
808 .into_iter()
809 .map(|policy| policy.try_into())
810 .collect::<Result<Vec<MembershipPolicies>, PolicyError>>()?;
811
812 Ok(MembershipPolicies::any(policies))
813 }
814 None => Err(PolicyError::InvalidMembershipPolicy),
815 }
816 }
817}
818
819impl MembershipPolicy for MembershipPolicies {
821 fn evaluate(&self, actor: &CommitParticipant, inbox: &Inbox) -> bool {
822 match self {
823 MembershipPolicies::Standard(policy) => policy.evaluate(actor, inbox),
824 MembershipPolicies::AndCondition(policy) => policy.evaluate(actor, inbox),
825 MembershipPolicies::AnyCondition(policy) => policy.evaluate(actor, inbox),
826 }
827 }
828
829 fn to_proto(&self) -> Result<MembershipPolicyProto, PolicyError> {
830 Ok(match self {
831 MembershipPolicies::Standard(policy) => policy.to_proto()?,
832 MembershipPolicies::AndCondition(policy) => policy.to_proto()?,
833 MembershipPolicies::AnyCondition(policy) => policy.to_proto()?,
834 })
835 }
836}
837
838#[derive(Clone, Debug, PartialEq)]
840pub struct AndCondition {
841 policies: Vec<MembershipPolicies>,
842}
843
844impl AndCondition {
845 pub(super) fn new(policies: Vec<MembershipPolicies>) -> Self {
846 Self { policies }
847 }
848}
849
850impl MembershipPolicy for AndCondition {
852 fn evaluate(&self, actor: &CommitParticipant, inbox: &Inbox) -> bool {
853 self.policies
854 .iter()
855 .all(|policy| policy.evaluate(actor, inbox))
856 }
857
858 fn to_proto(&self) -> Result<MembershipPolicyProto, PolicyError> {
859 Ok(MembershipPolicyProto {
860 kind: Some(PolicyKindProto::AndCondition(AndConditionProto {
861 policies: self
862 .policies
863 .iter()
864 .map(|policy| policy.to_proto())
865 .collect::<Result<Vec<MembershipPolicyProto>, PolicyError>>()?,
866 })),
867 })
868 }
869}
870
871#[derive(Clone, Debug, PartialEq)]
873pub struct AnyCondition {
874 policies: Vec<MembershipPolicies>,
875}
876
877#[allow(dead_code)]
878impl AnyCondition {
879 pub(super) fn new(policies: Vec<MembershipPolicies>) -> Self {
880 Self { policies }
881 }
882}
883
884impl MembershipPolicy for AnyCondition {
886 fn evaluate(&self, actor: &CommitParticipant, inbox: &Inbox) -> bool {
887 self.policies
888 .iter()
889 .any(|policy| policy.evaluate(actor, inbox))
890 }
891
892 fn to_proto(&self) -> Result<MembershipPolicyProto, PolicyError> {
893 Ok(MembershipPolicyProto {
894 kind: Some(PolicyKindProto::AnyCondition(AnyConditionProto {
895 policies: self
896 .policies
897 .iter()
898 .map(|policy| policy.to_proto())
899 .collect::<Result<Vec<MembershipPolicyProto>, PolicyError>>()?,
900 })),
901 })
902 }
903}
904
905#[derive(Debug, Clone, PartialEq)]
907#[allow(dead_code)]
908pub struct PolicySet {
909 pub add_member_policy: MembershipPolicies,
911 pub remove_member_policy: MembershipPolicies,
913 pub update_metadata_policy: HashMap<String, MetadataPolicies>,
915 pub add_admin_policy: PermissionsPolicies,
917 pub remove_admin_policy: PermissionsPolicies,
919 pub update_permissions_policy: PermissionsPolicies,
921}
922
923impl PolicySet {
924 pub fn new(
926 add_member_policy: MembershipPolicies,
927 remove_member_policy: MembershipPolicies,
928 update_metadata_policy: HashMap<String, MetadataPolicies>,
929 add_admin_policy: PermissionsPolicies,
930 remove_admin_policy: PermissionsPolicies,
931 update_permissions_policy: PermissionsPolicies,
932 ) -> Self {
933 Self {
934 add_member_policy,
935 remove_member_policy,
936 update_metadata_policy,
937 add_admin_policy,
938 remove_admin_policy,
939 update_permissions_policy,
940 }
941 }
942
943 pub fn new_dm() -> Self {
944 Self {
945 add_member_policy: MembershipPolicies::deny(),
946 remove_member_policy: MembershipPolicies::deny(),
947 update_metadata_policy: MetadataPolicies::dm_map(),
948 add_admin_policy: PermissionsPolicies::deny(),
949 remove_admin_policy: PermissionsPolicies::deny(),
950 update_permissions_policy: PermissionsPolicies::deny(),
951 }
952 }
953
954 pub fn evaluate_commit(&self, commit: &ValidatedCommit) -> bool {
962 let mut added_inboxes_valid = self.evaluate_policy_with_proposer(
965 commit.added_inboxes.iter(),
966 &self.add_member_policy,
967 &commit.actor,
968 );
969
970 if let Some(dm_members) = &commit.dm_members
972 && commit.added_inboxes.len() == 1
973 {
974 let added_inbox_id = &commit.added_inboxes[0].inbox_id;
975 if (added_inbox_id == &dm_members.member_one_inbox_id
976 || added_inbox_id == &dm_members.member_two_inbox_id)
977 && added_inbox_id != &commit.actor_inbox_id()
978 {
979 added_inboxes_valid = true;
980 }
981 }
982
983 let removed_inboxes_valid = self.evaluate_policy_with_proposer(
987 commit.removed_inboxes.iter(),
988 &self.remove_member_policy,
989 &commit.actor,
990 ) && !commit
991 .removed_inboxes
992 .iter()
993 .any(|inbox| inbox.is_super_admin);
994
995 let metadata_changes_valid = self.evaluate_metadata_policy(
1001 commit
1002 .metadata_validation_info
1003 .metadata_field_changes
1004 .iter(),
1005 &self.update_metadata_policy,
1006 &commit.actor,
1007 );
1008
1009 let admin_actor = &commit.actor;
1011 let added_admins_valid = commit.metadata_validation_info.admins_added.is_empty()
1012 || self.add_admin_policy.evaluate(admin_actor);
1013
1014 let removed_admins_valid = commit.metadata_validation_info.admins_removed.is_empty()
1016 || self.remove_admin_policy.evaluate(admin_actor);
1017
1018 let super_admin_add_valid = commit
1020 .metadata_validation_info
1021 .super_admins_added
1022 .is_empty()
1023 || admin_actor.is_super_admin;
1024
1025 let super_admin_remove_valid = commit
1028 .metadata_validation_info
1029 .super_admins_removed
1030 .is_empty()
1031 || (admin_actor.is_super_admin && commit.metadata_validation_info.num_super_admins > 0);
1032
1033 let permissions_changes_valid = !commit.permissions_changed || admin_actor.is_super_admin;
1036
1037 added_inboxes_valid
1038 && removed_inboxes_valid
1039 && metadata_changes_valid
1040 && added_admins_valid
1041 && removed_admins_valid
1042 && super_admin_add_valid
1043 && super_admin_remove_valid
1044 && permissions_changes_valid
1045 }
1046
1047 fn evaluate_policy_with_proposer<'a, I, P>(
1050 &self,
1051 mut changes: I,
1052 policy: &P,
1053 default_actor: &CommitParticipant,
1054 ) -> bool
1055 where
1056 I: Iterator<Item = &'a Inbox>,
1057 P: MembershipPolicy + std::fmt::Debug,
1058 {
1059 changes.all(|change| {
1060 let actor = change.proposer.as_ref().unwrap_or(default_actor);
1062 let is_ok = policy.evaluate(actor, change);
1063 if !is_ok {
1064 tracing::info!(
1065 "Policy {:?} failed for actor {:?} (proposer: {:?}) and change {:?}",
1066 policy,
1067 actor,
1068 change.proposer.is_some(),
1069 change
1070 );
1071 }
1072 is_ok
1073 })
1074 }
1075
1076 fn evaluate_metadata_policy<'a, I>(
1078 &self,
1079 mut changes: I,
1080 policies: &HashMap<String, MetadataPolicies>,
1081 actor: &CommitParticipant,
1082 ) -> bool
1083 where
1084 I: Iterator<Item = &'a MetadataFieldChange>,
1085 {
1086 changes.all(|change| {
1087 if let Some(policy) = policies.get(&change.field_name) {
1088 if !policy.evaluate(actor, change) {
1089 tracing::info!(
1090 "Policy for field {} failed for actor {:?} and change {:?}",
1091 change.field_name,
1092 actor,
1093 change
1094 );
1095 return false;
1096 }
1097 return true;
1098 }
1099 let policy_for_unrecognized_field =
1102 if change.field_name.starts_with(SUPER_ADMIN_METADATA_PREFIX) {
1103 MetadataPolicies::allow_if_actor_super_admin()
1104 } else {
1105 MetadataPolicies::allow_if_actor_admin()
1107 };
1108 if !policy_for_unrecognized_field.evaluate(actor, change) {
1109 tracing::info!(
1110 "Metadata field update with unknown policy was denied: {}",
1111 change.field_name
1112 );
1113 return false;
1114 }
1115 true
1116 })
1117 }
1118
1119 pub(crate) fn to_proto(&self) -> Result<PolicySetProto, PolicyError> {
1121 let add_member_policy = Some(self.add_member_policy.to_proto()?);
1122 let remove_member_policy = Some(self.remove_member_policy.to_proto()?);
1123
1124 let mut update_metadata_policy = HashMap::new();
1125 for (key, policy) in &self.update_metadata_policy {
1126 let policy_proto = policy.to_proto()?;
1127 update_metadata_policy.insert(key.clone(), policy_proto);
1128 }
1129 let add_admin_policy = Some(self.add_admin_policy.to_proto()?);
1130 let remove_admin_policy = Some(self.remove_admin_policy.to_proto()?);
1131 let update_permissions_policy = Some(self.update_permissions_policy.to_proto()?);
1132 Ok(PolicySetProto {
1133 add_member_policy,
1134 remove_member_policy,
1135 update_metadata_policy,
1136 add_admin_policy,
1137 remove_admin_policy,
1138 update_permissions_policy,
1139 })
1140 }
1141
1142 pub(crate) fn from_proto(proto: PolicySetProto) -> Result<Self, PolicyError> {
1144 let add_member_policy = MembershipPolicies::try_from(
1145 proto
1146 .add_member_policy
1147 .ok_or(PolicyError::FromProtoAddMemberInvalidPolicy)?,
1148 )?;
1149 let remove_member_policy = MembershipPolicies::try_from(
1150 proto
1151 .remove_member_policy
1152 .ok_or(PolicyError::FromProtoRemoveMemberInvalidPolicy)?,
1153 )?;
1154 let add_admin_policy = PermissionsPolicies::try_from(
1155 proto
1156 .add_admin_policy
1157 .ok_or(PolicyError::FromProtoAddAdminInvalidPolicy)?,
1158 )?;
1159 let remove_admin_policy = PermissionsPolicies::try_from(
1160 proto
1161 .remove_admin_policy
1162 .ok_or(PolicyError::FromProtoRemoveAdminInvalidPolicy)?,
1163 )?;
1164 let update_permissions_policy = PermissionsPolicies::try_from(
1165 proto
1166 .update_permissions_policy
1167 .ok_or(PolicyError::FromProtoUpdatePermissionsInvalidPolicy)?,
1168 )?;
1169
1170 let mut update_metadata_policy = HashMap::new();
1171 for (key, policy_proto) in proto.update_metadata_policy {
1172 let policy = MetadataPolicies::try_from(policy_proto)?;
1173 update_metadata_policy.insert(key, policy);
1174 }
1175 Ok(Self::new(
1176 add_member_policy,
1177 remove_member_policy,
1178 update_metadata_policy,
1179 add_admin_policy,
1180 remove_admin_policy,
1181 update_permissions_policy,
1182 ))
1183 }
1184
1185 pub fn to_bytes(&self) -> Result<Vec<u8>, PolicyError> {
1187 let proto = self.to_proto()?;
1188 let mut buf = Vec::new();
1189 proto.encode(&mut buf)?;
1190 Ok(buf)
1191 }
1192
1193 pub fn from_bytes(bytes: &[u8]) -> Result<Self, PolicyError> {
1195 let proto = PolicySetProto::decode(bytes)?;
1196 Self::from_proto(proto)
1197 }
1198}
1199
1200pub fn is_policy_default(policy: &PolicySet) -> Result<bool, PolicyError> {
1207 let mut metadata_policies_equal = true;
1208 for field_name in policy.update_metadata_policy.keys() {
1209 let metadata_policy = policy.update_metadata_policy.get(field_name).ok_or(
1210 PolicyError::MissingMetadataPolicyField {
1211 name: field_name.to_string(),
1212 },
1213 )?;
1214 if field_name == MetadataField::MessageDisappearInNS.as_str()
1215 || field_name == MetadataField::MessageDisappearFromNS.as_str()
1216 {
1217 metadata_policies_equal = metadata_policies_equal
1218 && metadata_policy.eq(&MetadataPolicies::allow_if_actor_admin());
1219 } else if field_name == MetadataField::MinimumSupportedProtocolVersion.as_str() {
1220 metadata_policies_equal = metadata_policies_equal
1221 && metadata_policy.eq(&MetadataPolicies::allow_if_actor_super_admin());
1222 } else {
1223 metadata_policies_equal =
1224 metadata_policies_equal && metadata_policy.eq(&MetadataPolicies::allow());
1225 }
1226 }
1227 Ok(metadata_policies_equal
1228 && policy.add_member_policy == MembershipPolicies::allow()
1229 && policy.remove_member_policy == MembershipPolicies::allow_if_actor_admin()
1230 && policy.add_admin_policy == PermissionsPolicies::allow_if_actor_super_admin()
1231 && policy.remove_admin_policy == PermissionsPolicies::allow_if_actor_super_admin()
1232 && policy.update_permissions_policy == PermissionsPolicies::allow_if_actor_super_admin())
1233}
1234
1235pub fn is_policy_admin_only(policy: &PolicySet) -> Result<bool, PolicyError> {
1242 let mut metadata_policies_equal = true;
1243 for field_name in policy.update_metadata_policy.keys() {
1244 let metadata_policy = policy.update_metadata_policy.get(field_name).ok_or(
1245 PolicyError::MissingMetadataPolicyField {
1246 name: field_name.to_string(),
1247 },
1248 )?;
1249 if field_name == MetadataField::MinimumSupportedProtocolVersion.as_str() {
1250 metadata_policies_equal = metadata_policies_equal
1251 && metadata_policy.eq(&MetadataPolicies::allow_if_actor_super_admin());
1252 } else {
1253 metadata_policies_equal = metadata_policies_equal
1254 && metadata_policy.eq(&MetadataPolicies::allow_if_actor_admin());
1255 }
1256 }
1257 Ok(metadata_policies_equal
1258 && policy.add_member_policy == MembershipPolicies::allow_if_actor_admin()
1259 && policy.remove_member_policy == MembershipPolicies::allow_if_actor_admin()
1260 && policy.add_admin_policy == PermissionsPolicies::allow_if_actor_super_admin()
1261 && policy.remove_admin_policy == PermissionsPolicies::allow_if_actor_super_admin()
1262 && policy.update_permissions_policy == PermissionsPolicies::allow_if_actor_super_admin())
1263}
1264
1265pub(crate) fn default_policy() -> PolicySet {
1269 let mut metadata_policies_map: HashMap<String, MetadataPolicies> = HashMap::new();
1270 for field in GroupMutableMetadata::supported_fields() {
1271 match field {
1272 MetadataField::MessageDisappearInNS => {
1273 metadata_policies_map
1274 .insert(field.to_string(), MetadataPolicies::allow_if_actor_admin());
1275 }
1276 MetadataField::MessageDisappearFromNS => {
1277 metadata_policies_map
1278 .insert(field.to_string(), MetadataPolicies::allow_if_actor_admin());
1279 }
1280 MetadataField::MinimumSupportedProtocolVersion => {
1281 metadata_policies_map.insert(
1282 field.to_string(),
1283 MetadataPolicies::allow_if_actor_super_admin(),
1284 );
1285 }
1286 _ => {
1287 metadata_policies_map.insert(field.to_string(), MetadataPolicies::allow());
1288 }
1289 }
1290 }
1291
1292 PolicySet::new(
1293 MembershipPolicies::allow(),
1294 MembershipPolicies::allow_if_actor_admin(),
1295 metadata_policies_map,
1296 PermissionsPolicies::allow_if_actor_super_admin(),
1297 PermissionsPolicies::allow_if_actor_super_admin(),
1298 PermissionsPolicies::allow_if_actor_super_admin(),
1299 )
1300}
1301
1302pub(crate) fn policy_admin_only() -> PolicySet {
1306 let mut metadata_policies_map: HashMap<String, MetadataPolicies> = HashMap::new();
1307 for field in GroupMutableMetadata::supported_fields() {
1308 match field {
1309 MetadataField::MessageDisappearInNS => {
1310 metadata_policies_map
1311 .insert(field.to_string(), MetadataPolicies::allow_if_actor_admin());
1312 }
1313 MetadataField::MessageDisappearFromNS => {
1314 metadata_policies_map
1315 .insert(field.to_string(), MetadataPolicies::allow_if_actor_admin());
1316 }
1317 MetadataField::MinimumSupportedProtocolVersion => {
1318 metadata_policies_map.insert(
1319 field.to_string(),
1320 MetadataPolicies::allow_if_actor_super_admin(),
1321 );
1322 }
1323 _ => {
1324 metadata_policies_map
1325 .insert(field.to_string(), MetadataPolicies::allow_if_actor_admin());
1326 }
1327 }
1328 }
1329
1330 PolicySet::new(
1331 MembershipPolicies::allow_if_actor_admin(),
1332 MembershipPolicies::allow_if_actor_admin(),
1333 metadata_policies_map,
1334 PermissionsPolicies::allow_if_actor_super_admin(),
1335 PermissionsPolicies::allow_if_actor_super_admin(),
1336 PermissionsPolicies::allow_if_actor_super_admin(),
1337 )
1338}
1339
1340impl Default for PolicySet {
1342 fn default() -> Self {
1343 PreconfiguredPolicies::default().to_policy_set()
1344 }
1345}
1346
1347#[derive(Debug, Clone, PartialEq, Default)]
1349pub enum PreconfiguredPolicies {
1350 #[default]
1352 Default,
1353 AdminsOnly,
1355}
1356
1357impl PreconfiguredPolicies {
1358 pub fn to_policy_set(&self) -> PolicySet {
1360 match self {
1361 PreconfiguredPolicies::Default => default_policy(),
1362 PreconfiguredPolicies::AdminsOnly => policy_admin_only(),
1363 }
1364 }
1365
1366 pub fn from_policy_set(policy_set: &PolicySet) -> Result<Self, PolicyError> {
1368 if is_policy_default(policy_set)? {
1369 Ok(PreconfiguredPolicies::Default)
1370 } else if is_policy_admin_only(policy_set)? {
1371 Ok(PreconfiguredPolicies::AdminsOnly)
1372 } else {
1373 Err(PolicyError::InvalidPresetPolicy)
1374 }
1375 }
1376}
1377
1378impl std::fmt::Display for PreconfiguredPolicies {
1380 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1381 write!(f, "{:?}", self)
1382 }
1383}
1384
1385#[cfg(test)]
1386pub(crate) mod tests {
1387 use std::collections::HashSet;
1388
1389 use crate::groups::validated_commit::MutableMetadataValidationInfo;
1390 use xmtp_common::{rand_string, rand_vec};
1391 use xmtp_mls_common::group_metadata::DmMembers;
1392
1393 use super::*;
1394
1395 fn build_change(inbox_id: Option<String>, is_admin: bool, is_super_admin: bool) -> Inbox {
1396 Inbox {
1397 inbox_id: inbox_id.unwrap_or(rand_string::<24>()),
1398 is_creator: is_super_admin,
1399 is_super_admin,
1400 is_admin,
1401 proposer: None,
1402 }
1403 }
1404
1405 fn build_actor(
1407 inbox_id: Option<String>,
1408 installation_id: Option<Vec<u8>>,
1409 is_admin: bool,
1410 is_super_admin: bool,
1411 ) -> CommitParticipant {
1412 CommitParticipant {
1413 inbox_id: inbox_id.unwrap_or(rand_string::<24>()),
1414 installation_id: installation_id.unwrap_or_else(rand_vec::<24>),
1415 is_creator: is_super_admin,
1416 is_admin,
1417 is_super_admin,
1418 }
1419 }
1420
1421 enum MemberType {
1422 SameAsActor,
1423 DmTarget,
1424 Random,
1425 }
1426
1427 fn build_validated_commit(
1429 member_added: Option<MemberType>,
1431 member_removed: Option<MemberType>,
1432 metadata_fields_changed: Option<Vec<String>>,
1433 permissions_changed: bool,
1434 actor_is_admin: bool,
1435 actor_is_super_admin: bool,
1436 dm_target_inbox_id: Option<String>,
1437 ) -> ValidatedCommit {
1438 let actor = build_actor(None, None, actor_is_admin, actor_is_super_admin);
1439 let dm_target_inbox_id_clone = dm_target_inbox_id.clone();
1440 let build_membership_change = |member_type: MemberType| match member_type {
1441 MemberType::SameAsActor => vec![build_change(
1442 Some(actor.inbox_id.clone()),
1443 actor_is_admin,
1444 actor_is_super_admin,
1445 )],
1446 MemberType::DmTarget => {
1447 vec![build_change(dm_target_inbox_id_clone.clone(), false, false)]
1448 }
1449 MemberType::Random => vec![build_change(None, false, false)],
1450 };
1451
1452 let field_changes = metadata_fields_changed
1453 .unwrap_or_default()
1454 .into_iter()
1455 .map(|field| {
1456 MetadataFieldChange::new(
1457 field,
1458 Some(rand_string::<24>()),
1459 Some(rand_string::<24>()),
1460 )
1461 })
1462 .collect();
1463
1464 let dm_members = if let Some(dm_target_inbox_id) = dm_target_inbox_id {
1465 Some(DmMembers {
1466 member_one_inbox_id: actor.inbox_id.clone(),
1467 member_two_inbox_id: dm_target_inbox_id,
1468 })
1469 } else {
1470 None
1471 };
1472
1473 ValidatedCommit {
1474 actor: actor.clone(),
1475 proposers: vec![actor.clone()], added_inboxes: member_added
1477 .map(build_membership_change)
1478 .unwrap_or_default(),
1479 removed_inboxes: member_removed
1480 .map(build_membership_change)
1481 .unwrap_or_default(),
1482 readded_installations: HashSet::new(),
1483 metadata_validation_info: MutableMetadataValidationInfo {
1484 metadata_field_changes: field_changes,
1485 ..Default::default()
1486 },
1487 installations_changed: false,
1488 permissions_changed,
1489 dm_members,
1490 }
1491 }
1492
1493 #[xmtp_common::test]
1496 fn test_allow_all() {
1497 let permissions = PolicySet::new(
1498 MembershipPolicies::allow(),
1499 MembershipPolicies::allow(),
1500 MetadataPolicies::default_map(MetadataPolicies::allow()),
1501 PermissionsPolicies::allow_if_actor_super_admin(),
1502 PermissionsPolicies::allow_if_actor_super_admin(),
1503 PermissionsPolicies::allow_if_actor_super_admin(),
1504 );
1505
1506 let commit = build_validated_commit(
1507 Some(MemberType::SameAsActor),
1508 Some(MemberType::SameAsActor),
1509 None,
1510 false,
1511 false,
1512 false,
1513 None,
1514 );
1515 assert!(permissions.evaluate_commit(&commit));
1516 }
1517
1518 #[xmtp_common::test]
1520 fn test_deny() {
1521 let permissions = PolicySet::new(
1522 MembershipPolicies::deny(),
1523 MembershipPolicies::deny(),
1524 MetadataPolicies::default_map(MetadataPolicies::deny()),
1525 PermissionsPolicies::allow_if_actor_super_admin(),
1526 PermissionsPolicies::allow_if_actor_super_admin(),
1527 PermissionsPolicies::allow_if_actor_super_admin(),
1528 );
1529
1530 let member_added_commit = build_validated_commit(
1531 Some(MemberType::Random),
1532 None,
1533 None,
1534 false,
1535 false,
1536 false,
1537 None,
1538 );
1539 assert!(!permissions.evaluate_commit(&member_added_commit));
1540
1541 let member_removed_commit = build_validated_commit(
1542 None,
1543 Some(MemberType::Random),
1544 None,
1545 false,
1546 false,
1547 false,
1548 None,
1549 );
1550 assert!(!permissions.evaluate_commit(&member_removed_commit));
1551 }
1552
1553 #[xmtp_common::test]
1555 fn test_actor_is_creator() {
1556 let permissions = PolicySet::new(
1557 MembershipPolicies::allow_if_actor_super_admin(),
1558 MembershipPolicies::allow_if_actor_super_admin(),
1559 MetadataPolicies::default_map(MetadataPolicies::deny()),
1560 PermissionsPolicies::allow_if_actor_super_admin(),
1561 PermissionsPolicies::allow_if_actor_super_admin(),
1562 PermissionsPolicies::allow_if_actor_super_admin(),
1563 );
1564
1565 let commit_with_creator = build_validated_commit(
1567 Some(MemberType::SameAsActor),
1568 Some(MemberType::SameAsActor),
1569 None,
1570 false,
1571 false,
1572 true,
1573 None,
1574 );
1575 assert!(!permissions.evaluate_commit(&commit_with_creator));
1576
1577 let commit_with_creator = build_validated_commit(
1578 Some(MemberType::SameAsActor),
1579 Some(MemberType::Random),
1580 None,
1581 false,
1582 false,
1583 true,
1584 None,
1585 );
1586 assert!(permissions.evaluate_commit(&commit_with_creator));
1587
1588 let commit_without_creator = build_validated_commit(
1589 Some(MemberType::SameAsActor),
1590 Some(MemberType::SameAsActor),
1591 None,
1592 false,
1593 false,
1594 false,
1595 None,
1596 );
1597 assert!(!permissions.evaluate_commit(&commit_without_creator));
1598 }
1599
1600 #[xmtp_common::test]
1602 fn test_and_condition() {
1603 let permissions = PolicySet::new(
1604 MembershipPolicies::and(vec![
1605 MembershipPolicies::Standard(BasePolicies::Deny),
1606 MembershipPolicies::Standard(BasePolicies::Allow),
1607 ]),
1608 MembershipPolicies::allow(),
1609 MetadataPolicies::default_map(MetadataPolicies::deny()),
1610 PermissionsPolicies::allow_if_actor_super_admin(),
1611 PermissionsPolicies::allow_if_actor_super_admin(),
1612 PermissionsPolicies::allow_if_actor_super_admin(),
1613 );
1614
1615 let member_added_commit = build_validated_commit(
1616 Some(MemberType::SameAsActor),
1617 None,
1618 None,
1619 false,
1620 false,
1621 false,
1622 None,
1623 );
1624 assert!(!permissions.evaluate_commit(&member_added_commit));
1625 }
1626
1627 #[xmtp_common::test]
1629 fn test_any_condition() {
1630 let permissions = PolicySet::new(
1631 MembershipPolicies::any(vec![
1632 MembershipPolicies::deny(),
1633 MembershipPolicies::allow(),
1634 ]),
1635 MembershipPolicies::allow(),
1636 MetadataPolicies::default_map(MetadataPolicies::deny()),
1637 PermissionsPolicies::allow_if_actor_super_admin(),
1638 PermissionsPolicies::allow_if_actor_super_admin(),
1639 PermissionsPolicies::allow_if_actor_super_admin(),
1640 );
1641
1642 let member_added_commit = build_validated_commit(
1643 Some(MemberType::SameAsActor),
1644 None,
1645 None,
1646 false,
1647 false,
1648 false,
1649 None,
1650 );
1651 assert!(permissions.evaluate_commit(&member_added_commit));
1652 }
1653
1654 #[xmtp_common::test]
1656 fn test_serialize() {
1657 let permissions = PolicySet::new(
1658 MembershipPolicies::any(vec![
1659 MembershipPolicies::allow(),
1660 MembershipPolicies::deny(),
1661 ]),
1662 MembershipPolicies::and(vec![
1663 MembershipPolicies::allow_if_actor_super_admin(),
1664 MembershipPolicies::deny(),
1665 ]),
1666 MetadataPolicies::default_map(MetadataPolicies::deny()),
1667 PermissionsPolicies::allow_if_actor_super_admin(),
1668 PermissionsPolicies::allow_if_actor_super_admin(),
1669 PermissionsPolicies::allow_if_actor_super_admin(),
1670 );
1671
1672 let proto = permissions.to_proto().unwrap();
1673 assert!(proto.add_member_policy.is_some());
1674 assert!(proto.remove_member_policy.is_some());
1675
1676 let as_bytes = permissions.to_bytes().expect("serialization failed");
1677 let restored = PolicySet::from_bytes(as_bytes.as_slice()).expect("proto conversion failed");
1678 assert!(permissions.eq(&restored))
1680 }
1681
1682 #[xmtp_common::test]
1684 fn test_update_group_name() {
1686 let allow_permissions = PolicySet::new(
1687 MembershipPolicies::allow(),
1688 MembershipPolicies::allow(),
1689 MetadataPolicies::default_map(MetadataPolicies::allow()),
1690 PermissionsPolicies::allow_if_actor_super_admin(),
1691 PermissionsPolicies::allow_if_actor_super_admin(),
1692 PermissionsPolicies::allow_if_actor_super_admin(),
1693 );
1694
1695 let member_added_commit = build_validated_commit(
1696 Some(MemberType::SameAsActor),
1697 None,
1698 Some(vec![MetadataField::GroupName.to_string()]),
1699 false,
1700 false,
1701 false,
1702 None,
1703 );
1704
1705 assert!(allow_permissions.evaluate_commit(&member_added_commit));
1706
1707 let deny_permissions = PolicySet::new(
1708 MembershipPolicies::allow(),
1709 MembershipPolicies::allow(),
1710 MetadataPolicies::default_map(MetadataPolicies::deny()),
1711 PermissionsPolicies::allow_if_actor_super_admin(),
1712 PermissionsPolicies::allow_if_actor_super_admin(),
1713 PermissionsPolicies::allow_if_actor_super_admin(),
1714 );
1715
1716 assert!(!deny_permissions.evaluate_commit(&member_added_commit));
1717 }
1718
1719 #[xmtp_common::test]
1721 fn test_preconfigured_policy() {
1722 let group_permissions = GroupMutablePermissions::new(default_policy());
1723
1724 assert_eq!(
1725 group_permissions.preconfigured_policy().unwrap(),
1726 PreconfiguredPolicies::Default
1727 );
1728
1729 let group_group_permissions_creator_admin =
1730 GroupMutablePermissions::new(policy_admin_only());
1731
1732 assert_eq!(
1733 group_group_permissions_creator_admin
1734 .preconfigured_policy()
1735 .unwrap(),
1736 PreconfiguredPolicies::AdminsOnly
1737 );
1738 }
1739
1740 #[xmtp_common::test]
1742 fn test_preconfigured_policy_equality_new_metadata() {
1743 let mut metadata_policies_map = MetadataPolicies::default_map(MetadataPolicies::allow());
1744 metadata_policies_map.insert("new_metadata_field".to_string(), MetadataPolicies::allow());
1745 let policy_set_new_metadata_permission = PolicySet {
1746 add_member_policy: MembershipPolicies::allow(),
1747 remove_member_policy: MembershipPolicies::allow_if_actor_admin(),
1748 update_metadata_policy: metadata_policies_map,
1749 add_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(),
1750 remove_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(),
1751 update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(),
1752 };
1753
1754 assert!(is_policy_default(&policy_set_new_metadata_permission).unwrap());
1755
1756 let mut metadata_policies_map =
1757 MetadataPolicies::default_map(MetadataPolicies::allow_if_actor_admin());
1758 metadata_policies_map.insert(
1759 "new_metadata_field_2".to_string(),
1760 MetadataPolicies::allow_if_actor_admin(),
1761 );
1762 let policy_set_new_metadata_permission = PolicySet {
1763 add_member_policy: MembershipPolicies::allow_if_actor_admin(),
1764 remove_member_policy: MembershipPolicies::allow_if_actor_admin(),
1765 update_metadata_policy: metadata_policies_map,
1766 add_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(),
1767 remove_admin_policy: PermissionsPolicies::allow_if_actor_super_admin(),
1768 update_permissions_policy: PermissionsPolicies::allow_if_actor_super_admin(),
1769 };
1770
1771 assert!(is_policy_admin_only(&policy_set_new_metadata_permission).unwrap());
1772 }
1773
1774 #[xmtp_common::test]
1776 fn test_permission_update() {
1777 let permissions = PolicySet::new(
1778 MembershipPolicies::allow(),
1779 MembershipPolicies::allow_if_actor_admin(),
1780 MetadataPolicies::default_map(MetadataPolicies::allow()),
1781 PermissionsPolicies::allow_if_actor_super_admin(),
1782 PermissionsPolicies::allow_if_actor_super_admin(),
1783 PermissionsPolicies::allow_if_actor_super_admin(),
1784 );
1785
1786 let commit = build_validated_commit(None, None, None, true, false, false, None);
1788 assert!(!permissions.evaluate_commit(&commit));
1789
1790 let commit = build_validated_commit(None, None, None, true, false, true, None);
1792 assert!(permissions.evaluate_commit(&commit));
1793 }
1794
1795 #[xmtp_common::test]
1797 fn test_evaluate_field_with_unknown_policy() {
1798 let permissions = PolicySet::new(
1800 MembershipPolicies::allow(),
1801 MembershipPolicies::allow(),
1802 MetadataPolicies::default_map(MetadataPolicies::allow()),
1803 PermissionsPolicies::allow_if_actor_super_admin(),
1804 PermissionsPolicies::allow_if_actor_super_admin(),
1805 PermissionsPolicies::allow_if_actor_super_admin(),
1806 );
1807
1808 let name_updated_commit = build_validated_commit(
1810 None,
1811 None,
1812 Some(vec![MetadataField::GroupName.to_string()]),
1813 false,
1814 false,
1815 false,
1816 None,
1817 );
1818 assert!(permissions.evaluate_commit(&name_updated_commit));
1819
1820 let non_existing_field_updated_commit = build_validated_commit(
1822 None,
1823 None,
1824 Some(vec!["non_existing_field".to_string()]),
1825 false,
1826 false,
1827 false,
1828 None,
1829 );
1830 assert!(!permissions.evaluate_commit(&non_existing_field_updated_commit));
1831
1832 let non_existing_field_updated_commit = build_validated_commit(
1834 None,
1835 None,
1836 Some(vec!["non_existing_field".to_string()]),
1837 false,
1838 true,
1839 false,
1840 None,
1841 );
1842 assert!(permissions.evaluate_commit(&non_existing_field_updated_commit));
1843
1844 let non_existing_field_updated_commit = build_validated_commit(
1846 None,
1847 None,
1848 Some(vec![
1849 SUPER_ADMIN_METADATA_PREFIX.to_string() + "non_existing_field",
1850 ]),
1851 false,
1852 true,
1853 false,
1854 None,
1855 );
1856 assert!(!permissions.evaluate_commit(&non_existing_field_updated_commit));
1857
1858 let non_existing_field_updated_commit = build_validated_commit(
1860 None,
1861 None,
1862 Some(vec![
1863 SUPER_ADMIN_METADATA_PREFIX.to_string() + "non_existing_field",
1864 ]),
1865 false,
1866 false,
1867 true,
1868 None,
1869 );
1870 assert!(permissions.evaluate_commit(&non_existing_field_updated_commit));
1871 }
1872
1873 #[xmtp_common::test]
1874 fn test_dm_group_permissions() {
1875 let permissions = PolicySet::new_dm();
1877
1878 const TARGET_INBOX_ID: &str = "example_target_dm_id";
1880
1881 let commit = build_validated_commit(
1883 Some(MemberType::Random),
1884 None,
1885 None,
1886 false,
1887 false,
1888 false,
1889 Some(TARGET_INBOX_ID.to_string()),
1890 );
1891 assert!(!permissions.evaluate_commit(&commit));
1892
1893 let commit = build_validated_commit(
1895 Some(MemberType::SameAsActor),
1896 None,
1897 None,
1898 false,
1899 false,
1900 false,
1901 Some(TARGET_INBOX_ID.to_string()),
1902 );
1903 assert!(!permissions.evaluate_commit(&commit));
1904
1905 let commit = build_validated_commit(
1907 Some(MemberType::DmTarget),
1908 None,
1909 None,
1910 false,
1911 false,
1912 false,
1913 Some(TARGET_INBOX_ID.to_string()),
1914 );
1915 assert!(permissions.evaluate_commit(&commit));
1916
1917 let commit = build_validated_commit(
1919 None,
1920 Some(MemberType::Random),
1921 None,
1922 false,
1923 false,
1924 false,
1925 Some(TARGET_INBOX_ID.to_string()),
1926 );
1927 assert!(!permissions.evaluate_commit(&commit));
1928 let commit = build_validated_commit(
1929 None,
1930 Some(MemberType::DmTarget),
1931 None,
1932 false,
1933 false,
1934 false,
1935 Some(TARGET_INBOX_ID.to_string()),
1936 );
1937 assert!(!permissions.evaluate_commit(&commit));
1938 let commit = build_validated_commit(
1939 None,
1940 Some(MemberType::SameAsActor),
1941 None,
1942 false,
1943 false,
1944 false,
1945 Some(TARGET_INBOX_ID.to_string()),
1946 );
1947 assert!(!permissions.evaluate_commit(&commit));
1948
1949 let commit = build_validated_commit(
1951 None,
1952 None,
1953 Some(vec![MetadataField::GroupName.to_string()]),
1954 false,
1955 false,
1956 false,
1957 Some(TARGET_INBOX_ID.to_string()),
1958 );
1959 assert!(permissions.evaluate_commit(&commit));
1960 }
1961}