Skip to content

Commit

Permalink
Added new Permissions Extension (#724) (#741)
Browse files Browse the repository at this point in the history
  • Loading branch information
neekolas authored May 15, 2024
2 parents 6e493e9 + 48063c1 commit dd39ca1
Show file tree
Hide file tree
Showing 14 changed files with 2,355 additions and 763 deletions.
2 changes: 2 additions & 0 deletions bindings_ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ pub enum GenericError {
Signature(#[from] xmtp_cryptography::signature::SignatureError),
#[error("Group metadata: {0}")]
GroupMetadata(#[from] xmtp_mls::groups::group_metadata::GroupMetadataError),
#[error("Group permissions: {0}")]
GroupMutablePermissions(#[from] xmtp_mls::groups::group_permissions::GroupMutablePermissionsError),
#[error("Generic {err}")]
Generic { err: String },
}
Expand Down
19 changes: 15 additions & 4 deletions bindings_ffi/src/mls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use xmtp_api_grpc::grpc_api_helper::Client as TonicApiClient;
use xmtp_id::InboxId;
use xmtp_mls::groups::group_metadata::ConversationType;
use xmtp_mls::groups::group_metadata::GroupMetadata;
use xmtp_mls::groups::group_permissions::GroupMutablePermissions;
use xmtp_mls::groups::PreconfiguredPolicies;
use xmtp_mls::identity::IdentityStrategy;
use xmtp_mls::{
Expand Down Expand Up @@ -181,8 +182,8 @@ pub enum GroupPermissions {
impl From<PreconfiguredPolicies> for GroupPermissions {
fn from(policy: PreconfiguredPolicies) -> Self {
match policy {
PreconfiguredPolicies::EveryoneIsAdmin => GroupPermissions::EveryoneIsAdmin,
PreconfiguredPolicies::GroupCreatorIsAdmin => GroupPermissions::GroupCreatorIsAdmin,
PreconfiguredPolicies::AllMembers => GroupPermissions::EveryoneIsAdmin,
PreconfiguredPolicies::AdminsOnly => GroupPermissions::GroupCreatorIsAdmin,
}
}
}
Expand All @@ -201,10 +202,10 @@ impl FfiConversations {

let group_permissions = match permissions {
Some(GroupPermissions::EveryoneIsAdmin) => {
Some(xmtp_mls::groups::PreconfiguredPolicies::EveryoneIsAdmin)
Some(xmtp_mls::groups::PreconfiguredPolicies::AllMembers)
}
Some(GroupPermissions::GroupCreatorIsAdmin) => {
Some(xmtp_mls::groups::PreconfiguredPolicies::GroupCreatorIsAdmin)
Some(xmtp_mls::groups::PreconfiguredPolicies::AdminsOnly)
}
_ => None,
};
Expand Down Expand Up @@ -660,6 +661,16 @@ impl FfiGroupMetadata {
ConversationType::Sync => "sync".to_string(),
}
}
}


#[derive(uniffi::Object)]
pub struct FfiGroupPermissions {
inner: Arc<GroupMutablePermissions>,
}

#[uniffi::export]
impl FfiGroupPermissions {

pub fn policy_type(&self) -> Result<GroupPermissions, GenericError> {
Ok(self.inner.preconfigured_policy()?.into())
Expand Down
6 changes: 2 additions & 4 deletions examples/cli/cli-client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,11 +301,9 @@ async fn main() {
}
Commands::CreateGroup { permissions } => {
let group_permissions = match permissions {
Permissions::EveryoneIsAdmin => {
xmtp_mls::groups::PreconfiguredPolicies::EveryoneIsAdmin
}
Permissions::EveryoneIsAdmin => xmtp_mls::groups::PreconfiguredPolicies::AllMembers,
Permissions::GroupCreatorIsAdmin => {
xmtp_mls::groups::PreconfiguredPolicies::GroupCreatorIsAdmin
xmtp_mls::groups::PreconfiguredPolicies::AdminsOnly
}
};
let client = create_client(&cli, IdentityStrategy::CachedOnly)
Expand Down
3 changes: 2 additions & 1 deletion examples/cli/serializable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ impl<'a> From<&'a MlsGroup> for SerializableGroup {
.collect::<Vec<String>>();

let metadata = group.metadata().expect("could not load metadata");
let permissions = group.permissions().expect("could not load permissions");

Self {
group_id,
members,
metadata: SerializableGroupMetadata {
creator_account_address: metadata.creator_account_address.clone(),
policy: metadata
policy: permissions
.preconfigured_policy()
.expect("could not get policy")
.to_string(),
Expand Down
1 change: 1 addition & 0 deletions xmtp_mls/src/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub const DELIMITER: char = '\x01';
/// | 0xff00 - 0xffff | Reserved for Private Use | N/A | N/A | RFC XXXX |
pub const MUTABLE_METADATA_EXTENSION_ID: u16 = 0xff00;
pub const GROUP_MEMBERSHIP_EXTENSION_ID: u16 = 0xff01;
pub const GROUP_PERMISSIONS_EXTENSION_ID: u16 = 0xff02;

pub const DEFAULT_GROUP_NAME: &str = "New Group";
pub const DEFAULT_GROUP_DESCRIPTION: &str = "New Group Description";
73 changes: 12 additions & 61 deletions xmtp_mls/src/groups/group_metadata.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,19 @@
use openmls::group::MlsGroup as OpenMlsGroup;
use openmls::{extensions::Extensions, group::MlsGroup as OpenMlsGroup};
use prost::Message;
use thiserror::Error;

use xmtp_proto::xmtp::mls::message_contents::{
ConversationType as ConversationTypeProto, GroupMetadataV1 as GroupMetadataProto,
};

use super::{
group_permissions::{PolicyError, PolicySet},
PreconfiguredPolicies,
};

#[derive(Debug, Error)]
pub enum GroupMetadataError {
#[error("serialization: {0}")]
Serialization(#[from] prost::EncodeError),
#[error("deserialization: {0}")]
Deserialization(#[from] prost::DecodeError),
#[error("policy error {0}")]
Policy(#[from] PolicyError),
#[error("invalid conversation type")]
InvalidConversationType,
#[error("missing policies")]
MissingPolicies,
#[error("missing extension")]
MissingExtension,
}
Expand All @@ -33,7 +24,6 @@ pub struct GroupMetadata {
// TODO: Remove this once transition is completed
pub creator_account_address: String,
pub creator_inbox_id: String,
pub policies: PolicySet,
}

impl GroupMetadata {
Expand All @@ -42,33 +32,19 @@ impl GroupMetadata {
// TODO: Remove this once transition is completed
creator_account_address: String,
creator_inbox_id: String,
policies: PolicySet,
) -> Self {
Self {
conversation_type,
creator_account_address,
creator_inbox_id,
policies,
}
}

#[allow(dead_code)]
// TODO:nm decide if we need to remove
pub fn preconfigured_policy(&self) -> Result<PreconfiguredPolicies, GroupMetadataError> {
Ok(PreconfiguredPolicies::from_policy_set(&self.policies)?)
}

pub(crate) fn from_proto(proto: GroupMetadataProto) -> Result<Self, GroupMetadataError> {
if proto.policies.is_none() {
return Err(GroupMetadataError::MissingPolicies);
}
let policies = proto.policies.unwrap();

Ok(Self::new(
proto.conversation_type.try_into()?,
proto.creator_account_address.clone(),
proto.creator_inbox_id.clone(),
PolicySet::from_proto(policies)?,
))
}

Expand All @@ -78,7 +54,6 @@ impl GroupMetadata {
conversation_type: conversation_type as i32,
creator_inbox_id: self.creator_inbox_id.clone(),
creator_account_address: self.creator_account_address.clone(),
policies: Some(self.policies.to_proto()?),
})
}
}
Expand Down Expand Up @@ -112,6 +87,17 @@ impl TryFrom<GroupMetadataProto> for GroupMetadata {
}
}

impl TryFrom<&Extensions> for GroupMetadata {
type Error = GroupMetadataError;

fn try_from(extensions: &Extensions) -> Result<Self, Self::Error> {
let data = extensions
.immutable_metadata()
.ok_or(GroupMetadataError::MissingExtension)?;
data.metadata().try_into()
}
}

#[derive(Debug, Clone, PartialEq)]
pub enum ConversationType {
Group,
Expand Down Expand Up @@ -151,38 +137,3 @@ pub fn extract_group_metadata(group: &OpenMlsGroup) -> Result<GroupMetadata, Gro

extension.metadata().try_into()
}

#[cfg(test)]
mod tests {
use crate::groups::group_permissions::{
policy_everyone_is_admin, policy_group_creator_is_admin,
};

use super::*;
#[test]
fn test_preconfigured_policy() {
let account_address = "account_address";
let group_metadata = GroupMetadata::new(
ConversationType::Group,
account_address.to_string(),
"inbox_id".to_string(),
policy_everyone_is_admin(),
);
assert_eq!(
group_metadata.preconfigured_policy().unwrap(),
PreconfiguredPolicies::EveryoneIsAdmin
);

let group_metadata_creator_admin = GroupMetadata::new(
ConversationType::Group,
account_address.to_string(),
"inbox_id".to_string(),
policy_group_creator_is_admin(),
);

assert_eq!(
group_metadata_creator_admin.preconfigured_policy().unwrap(),
PreconfiguredPolicies::GroupCreatorIsAdmin
);
}
}
68 changes: 57 additions & 11 deletions xmtp_mls/src/groups/group_mutable_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ use openmls::{
use prost::Message;
use thiserror::Error;

use xmtp_proto::xmtp::mls::message_contents::GroupMutableMetadataV1 as GroupMutableMetadataProto;
use xmtp_proto::xmtp::mls::message_contents::{
GroupMutableMetadataV1 as GroupMutableMetadataProto, Inboxes as InboxesProto,
};

use crate::configuration::{
DEFAULT_GROUP_DESCRIPTION, DEFAULT_GROUP_NAME, MUTABLE_METADATA_EXTENSION_ID,
Expand All @@ -27,6 +29,8 @@ pub enum GroupMutableMetadataError {
TooManyUpdates,
#[error("no changes in this update")]
NoUpdates,
#[error("metadata field is missing")]
MissingMetadataField,
}

// Fields should be added to supported_fields fn for Metadata Update Support
Expand Down Expand Up @@ -55,10 +59,24 @@ impl fmt::Display for MetadataField {
pub struct GroupMutableMetadata {
// Allow libxmtp to receive attributes from updated versions not yet captured in MetadataField
pub attributes: HashMap<String, String>,
pub admin_list: Vec<String>,
pub super_admin_list: Vec<String>,
}

impl Default for GroupMutableMetadata {
fn default() -> Self {
impl GroupMutableMetadata {
pub fn new(
attributes: HashMap<String, String>,
admin_list: Vec<String>,
super_admin_list: Vec<String>,
) -> Self {
Self {
attributes,
admin_list,
super_admin_list,
}
}

pub fn new_default(creator_inbox_id: String) -> Self {
let mut attributes = HashMap::new();
attributes.insert(
MetadataField::GroupName.to_string(),
Expand All @@ -68,19 +86,27 @@ impl Default for GroupMutableMetadata {
MetadataField::Description.to_string(),
DEFAULT_GROUP_DESCRIPTION.to_string(),
);
Self { attributes }
}
}

impl GroupMutableMetadata {
pub fn new(attributes: HashMap<String, String>) -> Self {
Self { attributes }
let admin_list = vec![creator_inbox_id.clone()];
let super_admin_list = vec![creator_inbox_id.clone()];
Self {
attributes,
admin_list,
super_admin_list,
}
}

// These fields will receive default permission policies for new groups
pub fn supported_fields() -> Vec<MetadataField> {
vec![MetadataField::GroupName, MetadataField::Description]
}

pub fn is_admin(&self, inbox_id: &String) -> bool {
self.admin_list.contains(inbox_id)
}

pub fn is_super_admin(&self, inbox_id: &String) -> bool {
self.super_admin_list.contains(inbox_id)
}
}

impl TryFrom<GroupMutableMetadata> for Vec<u8> {
Expand All @@ -90,6 +116,12 @@ impl TryFrom<GroupMutableMetadata> for Vec<u8> {
let mut buf = Vec::new();
let proto_val = GroupMutableMetadataProto {
attributes: value.attributes.clone(),
admin_list: Some(InboxesProto {
inbox_ids: value.admin_list,
}),
super_admin_list: Some(InboxesProto {
inbox_ids: value.super_admin_list,
}),
};
proto_val.encode(&mut buf)?;

Expand All @@ -110,7 +142,21 @@ impl TryFrom<GroupMutableMetadataProto> for GroupMutableMetadata {
type Error = GroupMutableMetadataError;

fn try_from(value: GroupMutableMetadataProto) -> Result<Self, Self::Error> {
Ok(Self::new(value.attributes.clone()))
let admin_list = value
.admin_list
.ok_or_else(|| GroupMutableMetadataError::MissingMetadataField)?
.inbox_ids;

let super_admin_list = value
.super_admin_list
.ok_or_else(|| GroupMutableMetadataError::MissingMetadataField)?
.inbox_ids;

Ok(Self::new(
value.attributes.clone(),
admin_list,
super_admin_list,
))
}
}

Expand Down
Loading

0 comments on commit dd39ca1

Please sign in to comment.