Skip to content

Commit

Permalink
Consent State Group & Member Associations and Bindings (#1057)
Browse files Browse the repository at this point in the history
* add consent to the group and member tables

* add the consent to the members

* add consent to new groups

* add the consent state to the stored group

* expose the consent state on the bindings

* add method for updating the consent state

* add the consent state to the group

* add binding for updating consent on a group

* add set methods

* add bindings for fetching the consent state

* remove the clone call

* remove some unwraps

* update the clippy

* cargo fmt

* clean up the translation code to use into

* Update xmtp_mls/src/groups/mod.rs

Co-authored-by: Andrew Plaza <github.tech@liquidthink.net>

* map the error

* add a test for group consent to the bindings

* add a test for address and inbox consent

* handle the address versus inboxid

* get the tests failing more accuratly

* remove the field on group so that there is just one table source of truth

* add default of allowed

* get one of the tests passing

* get both tests passing

* cargo clippy

* write a test for the client

* small tweaks in clean up

* small tweaks in clean up

* add a final test

* cargo fmt

* make the test more stable

---------

Co-authored-by: Andrew Plaza <github.tech@liquidthink.net>
  • Loading branch information
nplasterer and insipx authored Sep 13, 2024
1 parent 6b7a163 commit 7e70ad4
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 13 deletions.
4 changes: 2 additions & 2 deletions bindings_ffi/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

182 changes: 178 additions & 4 deletions bindings_ffi/src/mls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ use xmtp_mls::groups::group_permissions::PolicySet;
use xmtp_mls::groups::intents::PermissionPolicyOption;
use xmtp_mls::groups::intents::PermissionUpdateType;
use xmtp_mls::groups::GroupMetadataOptions;
use xmtp_mls::storage::consent_record::ConsentState;
use xmtp_mls::storage::consent_record::ConsentType;
use xmtp_mls::{
api::ApiClientWrapper,
builder::ClientBuilder,
Expand Down Expand Up @@ -328,6 +330,30 @@ impl FfiXmtpClient {
.await?;
Ok(state.into())
}

pub async fn set_consent_state(
&self,
state: FfiConsentState,
entity_type: FfiConsentEntityType,
entity: String,
) -> Result<(), GenericError> {
let inner = self.inner_client.as_ref();
inner
.set_consent_state(state.into(), entity_type.into(), entity)
.await?;
Ok(())
}

pub async fn get_consent_state(
&self,
entity_type: FfiConsentEntityType,
entity: String,
) -> Result<FfiConsentState, GenericError> {
let inner = self.inner_client.as_ref();
let result = inner.get_consent_state(entity_type.into(), entity).await?;

Ok(result.into())
}
}

#[uniffi::export(async_runtime = "tokio")]
Expand Down Expand Up @@ -843,6 +869,7 @@ pub struct FfiGroupMember {
pub account_addresses: Vec<String>,
pub installation_ids: Vec<Vec<u8>>,
pub permission_level: FfiPermissionLevel,
pub consent_state: FfiConsentState,
}

#[derive(uniffi::Enum)]
Expand All @@ -852,6 +879,50 @@ pub enum FfiPermissionLevel {
SuperAdmin,
}

#[derive(uniffi::Enum, PartialEq, Debug)]
pub enum FfiConsentState {
Unknown,
Allowed,
Denied,
}

impl From<ConsentState> for FfiConsentState {
fn from(state: ConsentState) -> Self {
match state {
ConsentState::Unknown => FfiConsentState::Unknown,
ConsentState::Allowed => FfiConsentState::Allowed,
ConsentState::Denied => FfiConsentState::Denied,
}
}
}

impl From<FfiConsentState> for ConsentState {
fn from(state: FfiConsentState) -> Self {
match state {
FfiConsentState::Unknown => ConsentState::Unknown,
FfiConsentState::Allowed => ConsentState::Allowed,
FfiConsentState::Denied => ConsentState::Denied,
}
}
}

#[derive(uniffi::Enum)]
pub enum FfiConsentEntityType {
GroupId,
InboxId,
Address,
}

impl From<FfiConsentEntityType> for ConsentType {
fn from(entity_type: FfiConsentEntityType) -> Self {
match entity_type {
FfiConsentEntityType::GroupId => ConsentType::GroupId,
FfiConsentEntityType::InboxId => ConsentType::InboxId,
FfiConsentEntityType::Address => ConsentType::Address,
}
}
}

#[derive(uniffi::Record, Clone, Default)]
pub struct FfiListMessagesOptions {
pub sent_before_ns: Option<i64>,
Expand Down Expand Up @@ -995,6 +1066,7 @@ impl FfiGroup {
PermissionLevel::Admin => FfiPermissionLevel::Admin,
PermissionLevel::SuperAdmin => FfiPermissionLevel::SuperAdmin,
},
consent_state: member.consent_state.into(),
})
.collect();

Expand Down Expand Up @@ -1333,6 +1405,30 @@ impl FfiGroup {
Ok(group.is_active(group.mls_provider()?)?)
}

pub fn consent_state(&self) -> Result<FfiConsentState, GenericError> {
let group = MlsGroup::new(
self.inner_client.context().clone(),
self.group_id.clone(),
self.created_at_ns,
);

let state = group.consent_state()?;

Ok(state.into())
}

pub fn update_consent_state(&self, state: FfiConsentState) -> Result<(), GenericError> {
let group = MlsGroup::new(
self.inner_client.context().clone(),
self.group_id.clone(),
self.created_at_ns,
);

group.update_consent_state(state.into())?;

Ok(())
}

pub fn added_by_inbox_id(&self) -> Result<String, GenericError> {
let group = MlsGroup::new(
self.inner_client.context().clone(),
Expand Down Expand Up @@ -1571,10 +1667,10 @@ mod tests {
use super::{create_client, signature_verifier, FfiMessage, FfiMessageCallback, FfiXmtpClient};
use crate::{
get_inbox_id_for_address, inbox_owner::SigningError, logger::FfiLogger,
FfiConversationCallback, FfiCreateGroupOptions, FfiGroup, FfiGroupMessageKind,
FfiGroupPermissionsOptions, FfiInboxOwner, FfiListConversationsOptions,
FfiListMessagesOptions, FfiMetadataField, FfiPermissionPolicy, FfiPermissionPolicySet,
FfiPermissionUpdateType,
FfiConsentEntityType, FfiConsentState, FfiConversationCallback, FfiCreateGroupOptions,
FfiGroup, FfiGroupMessageKind, FfiGroupPermissionsOptions, FfiInboxOwner,
FfiListConversationsOptions, FfiListMessagesOptions, FfiMetadataField, FfiPermissionPolicy,
FfiPermissionPolicySet, FfiPermissionUpdateType,
};
use ethers::utils::hex;
use rand::distributions::{Alphanumeric, DistString};
Expand Down Expand Up @@ -3580,4 +3676,82 @@ mod tests {
client_1.installation_id()
);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
async fn test_set_and_get_group_consent() {
let alix = new_test_client().await;
let bo = new_test_client().await;

let alix_group = alix
.conversations()
.create_group(
vec![bo.account_address.clone()],
FfiCreateGroupOptions::default(),
)
.await
.unwrap();

let alix_initial_consent = alix_group.consent_state().unwrap();
assert_eq!(alix_initial_consent, FfiConsentState::Allowed);

bo.conversations().sync().await.unwrap();
let bo_group = bo.group(alix_group.id()).unwrap();

let bo_initial_consent = bo_group.consent_state().unwrap();
assert_eq!(bo_initial_consent, FfiConsentState::Unknown);

alix_group
.update_consent_state(FfiConsentState::Denied)
.unwrap();
let alix_updated_consent = alix_group.consent_state().unwrap();
assert_eq!(alix_updated_consent, FfiConsentState::Denied);

bo.set_consent_state(
FfiConsentState::Allowed,
FfiConsentEntityType::GroupId,
hex::encode(bo_group.id()),
)
.await
.unwrap();
let bo_updated_consent = bo_group.consent_state().unwrap();
assert_eq!(bo_updated_consent, FfiConsentState::Allowed);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
async fn test_set_and_get_member_consent() {
let alix = new_test_client().await;
let bo = new_test_client().await;

let alix_group = alix
.conversations()
.create_group(
vec![bo.account_address.clone()],
FfiCreateGroupOptions::default(),
)
.await
.unwrap();
alix.set_consent_state(
FfiConsentState::Allowed,
FfiConsentEntityType::Address,
bo.account_address.clone(),
)
.await
.unwrap();
let bo_consent = alix
.get_consent_state(FfiConsentEntityType::Address, bo.account_address.clone())
.await
.unwrap();
assert_eq!(bo_consent, FfiConsentState::Allowed);

if let Some(member) = alix_group
.list_members()
.unwrap()
.iter()
.find(|&m| m.inbox_id == bo.inbox_id())
{
assert_eq!(member.consent_state, FfiConsentState::Allowed);
} else {
panic!("Error: No member found with the given inbox_id.");
}
}
}
80 changes: 80 additions & 0 deletions xmtp_mls/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ use crate::{
retry::Retry,
retry_async, retryable,
storage::{
consent_record::{ConsentState, ConsentType, StoredConsentRecord},
db_connection::DbConnection,
group::{GroupMembershipState, StoredGroup},
group_message::StoredGroupMessage,
Expand Down Expand Up @@ -339,6 +340,58 @@ where
Ok(state)
}

// set the consent record in the database
// if the consent record is an address also set the inboxId
pub async fn set_consent_state(
&self,
state: ConsentState,
entity_type: ConsentType,
entity: String,
) -> Result<(), ClientError> {
let conn = self.store().conn()?;
conn.insert_or_replace_consent_record(StoredConsentRecord::new(
entity_type,
state,
entity.clone(),
))?;

if entity_type == ConsentType::Address {
if let Some(inbox_id) = self.find_inbox_id_from_address(entity.clone()).await? {
conn.insert_or_replace_consent_record(StoredConsentRecord::new(
ConsentType::InboxId,
state,
inbox_id,
))?;
}
};

Ok(())
}

// get the consent record from the database
// if the consent record is an address also get the inboxId instead
pub async fn get_consent_state(
&self,
entity_type: ConsentType,
entity: String,
) -> Result<ConsentState, ClientError> {
let conn = self.store().conn()?;
let record = if entity_type == ConsentType::Address {
if let Some(inbox_id) = self.find_inbox_id_from_address(entity.clone()).await? {
conn.get_consent_record(inbox_id, ConsentType::InboxId)?
} else {
conn.get_consent_record(entity, entity_type)?
}
} else {
conn.get_consent_record(entity, entity_type)?
};

match record {
Some(rec) => Ok(rec.state),
None => Ok(ConsentState::Unknown),
}
}

pub fn store(&self) -> &EncryptedMessageStore {
&self.context.store
}
Expand Down Expand Up @@ -763,6 +816,7 @@ mod tests {
builder::ClientBuilder,
groups::GroupMetadataOptions,
hpke::{decrypt_welcome, encrypt_welcome},
storage::consent_record::{ConsentState, ConsentType},
};

#[tokio::test]
Expand Down Expand Up @@ -1040,4 +1094,30 @@ mod tests {
vec![1, 2, 3]
)
}

#[tokio::test]
async fn test_get_and_set_consent() {
let bo_wallet = generate_local_wallet();
let alix = ClientBuilder::new_test_client(&generate_local_wallet()).await;
let bo = ClientBuilder::new_test_client(&bo_wallet).await;

alix.set_consent_state(
ConsentState::Denied,
ConsentType::Address,
bo_wallet.get_address(),
)
.await
.unwrap();
let inbox_consent = alix
.get_consent_state(ConsentType::InboxId, bo.inbox_id())
.await
.unwrap();
let address_consent = alix
.get_consent_state(ConsentType::Address, bo_wallet.get_address())
.await
.unwrap();

assert_eq!(inbox_consent, ConsentState::Denied);
assert_eq!(address_consent, ConsentState::Denied);
}
}
13 changes: 11 additions & 2 deletions xmtp_mls/src/groups/members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ use xmtp_id::InboxId;
use super::{validated_commit::extract_group_membership, GroupError, MlsGroup};

use crate::{
storage::association_state::StoredAssociationState, xmtp_openmls_provider::XmtpOpenMlsProvider,
storage::{
association_state::StoredAssociationState,
consent_record::{ConsentState, ConsentType},
},
xmtp_openmls_provider::XmtpOpenMlsProvider,
};

#[derive(Debug, Clone)]
Expand All @@ -12,6 +16,7 @@ pub struct GroupMember {
pub account_addresses: Vec<String>,
pub installation_ids: Vec<Vec<u8>>,
pub permission_level: PermissionLevel,
pub consent_state: ConsentState,
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -71,11 +76,15 @@ impl MlsGroup {
PermissionLevel::Member
};

let consent =
conn.get_consent_record(inbox_id_str.clone(), ConsentType::InboxId)?;

Ok(GroupMember {
inbox_id: inbox_id_str,
inbox_id: inbox_id_str.clone(),
account_addresses: association_state.account_addresses(),
installation_ids: association_state.installation_ids(),
permission_level,
consent_state: consent.map_or(ConsentState::Unknown, |c| c.state),
})
})
.collect::<Result<Vec<GroupMember>, GroupError>>()?;
Expand Down
Loading

0 comments on commit 7e70ad4

Please sign in to comment.