Skip to content

Commit

Permalink
Allow custom permissions on group creation (#277)
Browse files Browse the repository at this point in the history
* Update bindings for libxmtp 4a572e8a

* add custom policy parameter

* adds optional permission policy set on group creation

* lint fix

* remove extra comment

* adds newGroupCustomPermissions function for clarity

* remove unused import

* make parameter ordering more consistent

---------

Co-authored-by: cameronvoell <cameronvoell@users.noreply.github.com>
  • Loading branch information
cameronvoell and cameronvoell authored Jul 24, 2024
1 parent 13fbc95 commit 1367923
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import org.xmtp.android.library.libxmtp.PermissionLevel
import org.xmtp.android.library.messages.PrivateKey
import org.xmtp.android.library.messages.PrivateKeyBuilder
import org.xmtp.android.library.messages.walletAddress
import uniffi.xmtpv3.GenericException
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionOption
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionPolicySet
import java.security.SecureRandom

@RunWith(AndroidJUnit4::class)
Expand Down Expand Up @@ -368,4 +370,85 @@ class GroupPermissionsTest {
assert(boGroup.pinnedFrameUrl == "new pinned frame url 2")
assert(alixGroup.pinnedFrameUrl == "new pinned frame url 2")
}

@Test
fun canCreateGroupWithCustomPermissions() {
val permissionPolicySet = PermissionPolicySet(
addMemberPolicy = PermissionOption.Admin,
removeMemberPolicy = PermissionOption.Deny,
addAdminPolicy = PermissionOption.Admin,
removeAdminPolicy = PermissionOption.SuperAdmin,
updateGroupNamePolicy = PermissionOption.Admin,
updateGroupDescriptionPolicy = PermissionOption.Allow,
updateGroupImagePolicy = PermissionOption.Admin,
updateGroupPinnedFrameUrlPolicy = PermissionOption.Deny,
)
val boGroup = runBlocking {
boClient.conversations.newGroupCustomPermissions(
accountAddresses = listOf(alix.walletAddress, caro.walletAddress),
permissionPolicySet = permissionPolicySet,
)
}
runBlocking { alixClient.conversations.syncGroups() }
val alixGroup = runBlocking { alixClient.conversations.listGroups().first() }

// Verify permission look correct
val alixPermissionSet = alixGroup.permissionPolicySet()
assert(alixPermissionSet.addMemberPolicy == PermissionOption.Admin)
assert(alixPermissionSet.removeMemberPolicy == PermissionOption.Deny)
assert(alixPermissionSet.addAdminPolicy == PermissionOption.Admin)
assert(alixPermissionSet.removeAdminPolicy == PermissionOption.SuperAdmin)
assert(alixPermissionSet.updateGroupNamePolicy == PermissionOption.Admin)
assert(alixPermissionSet.updateGroupDescriptionPolicy == PermissionOption.Allow)
assert(alixPermissionSet.updateGroupImagePolicy == PermissionOption.Admin)
assert(alixPermissionSet.updateGroupPinnedFrameUrlPolicy == PermissionOption.Deny)
}

@Test
fun createGroupWithInvalidCustomPermissionsFails() {
// Add/Remove Admin can not be allow
val permissionPolicySetInvalid = PermissionPolicySet(
addMemberPolicy = PermissionOption.Admin,
removeMemberPolicy = PermissionOption.Deny,
addAdminPolicy = PermissionOption.Admin,
removeAdminPolicy = PermissionOption.Allow,
updateGroupNamePolicy = PermissionOption.Admin,
updateGroupDescriptionPolicy = PermissionOption.Allow,
updateGroupImagePolicy = PermissionOption.Admin,
updateGroupPinnedFrameUrlPolicy = PermissionOption.Deny,
)

assertThrows(GenericException.GroupMutablePermissions::class.java) {
val boGroup = runBlocking {
boClient.conversations.newGroupCustomPermissions(
accountAddresses = listOf(alix.walletAddress, caro.walletAddress),
permissionPolicySet = permissionPolicySetInvalid,
)
}
}

val permissionPolicySetValid = PermissionPolicySet(
addMemberPolicy = PermissionOption.Admin,
removeMemberPolicy = PermissionOption.Deny,
addAdminPolicy = PermissionOption.Admin,
removeAdminPolicy = PermissionOption.SuperAdmin,
updateGroupNamePolicy = PermissionOption.Admin,
updateGroupDescriptionPolicy = PermissionOption.Allow,
updateGroupImagePolicy = PermissionOption.Admin,
updateGroupPinnedFrameUrlPolicy = PermissionOption.Deny,
)

// Valid custom policy works as expected
runBlocking { alixClient.conversations.syncGroups() }
assert(runBlocking { alixClient.conversations.listGroups() }.isEmpty())

val boGroup = runBlocking {
boClient.conversations.newGroupCustomPermissions(
accountAddresses = listOf(alix.walletAddress, caro.walletAddress),
permissionPolicySet = permissionPolicySetValid,
)
}
runBlocking { alixClient.conversations.syncGroups() }
assert(runBlocking { alixClient.conversations.listGroups() }.size == 1)
}
}
4 changes: 2 additions & 2 deletions library/src/main/java/libxmtp-version.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
Version: d1cd7939
Version: 4a572e8a
Branch: main
Date: 2024-07-19 21:33:48 +0000
Date: 2024-07-23 15:16:12 +0000
50 changes: 47 additions & 3 deletions library/src/main/java/org/xmtp/android/library/Conversations.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ import uniffi.xmtpv3.FfiV2Subscription
import uniffi.xmtpv3.FfiV2SubscriptionCallback
import uniffi.xmtpv3.NoPointer
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.GroupPermissionPreconfiguration
import uniffi.xmtpv3.org.xmtp.android.library.libxmtp.PermissionPolicySet
import uniffi.xmtpv3.FfiGroupPermissionsOptions
import uniffi.xmtpv3.FfiPermissionPolicySet
import java.util.Date
import kotlin.time.Duration.Companion.nanoseconds
import kotlin.time.DurationUnit
Expand Down Expand Up @@ -113,7 +116,47 @@ data class Conversations(
groupName: String = "",
groupImageUrlSquare: String = "",
groupDescription: String = "",
groupPinnedFrameUrl: String = ""
groupPinnedFrameUrl: String = "",
): Group {
return newGroupInternal(
accountAddresses,
GroupPermissionPreconfiguration.toFfiGroupPermissionOptions(permissions),
groupName,
groupImageUrlSquare,
groupDescription,
groupPinnedFrameUrl,
null
)
}

suspend fun newGroupCustomPermissions(
accountAddresses: List<String>,
permissionPolicySet: PermissionPolicySet,
groupName: String = "",
groupImageUrlSquare: String = "",
groupDescription: String = "",
groupPinnedFrameUrl: String = "",

): Group {
return newGroupInternal(
accountAddresses,
FfiGroupPermissionsOptions.CUSTOM_POLICY,
groupName,
groupImageUrlSquare,
groupDescription,
groupPinnedFrameUrl,
PermissionPolicySet.toFfiPermissionPolicySet(permissionPolicySet)
)
}

private suspend fun newGroupInternal(
accountAddresses: List<String>,
permissions: FfiGroupPermissionsOptions,
groupName: String,
groupImageUrlSquare: String,
groupDescription: String,
groupPinnedFrameUrl: String,
permissionsPolicySet: FfiPermissionPolicySet?
): Group {
if (accountAddresses.size == 1 &&
accountAddresses.first().lowercase() == client.address.lowercase()
Expand All @@ -131,11 +174,12 @@ data class Conversations(
libXMTPConversations?.createGroup(
accountAddresses,
opts = FfiCreateGroupOptions(
permissions = GroupPermissionPreconfiguration.toFfiGroupPermissionOptions(permissions),
permissions = permissions,
groupName = groupName,
groupImageUrlSquare = groupImageUrlSquare,
groupDescription = groupDescription,
groupPinnedFrameUrl = groupPinnedFrameUrl
groupPinnedFrameUrl = groupPinnedFrameUrl,
customPermissionPolicySet = permissionsPolicySet
)
) ?: throw XMTPException("Client does not support Groups")
client.contacts.allowGroups(groupIds = listOf(group.id().toHex()))
Expand Down
2 changes: 1 addition & 1 deletion library/src/main/java/org/xmtp/android/library/Group.kt
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) {
}

fun permissionPolicySet(): PermissionPolicySet {
return PermissionPolicySet(permissions.policySet())
return PermissionPolicySet.fromFfiPermissionPolicySet(permissions.policySet())
}

fun creatorInboxId(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,41 @@ enum class GroupPermissionPreconfiguration {
}
}

class PermissionPolicySet(private val ffiPermissionPolicySet: FfiPermissionPolicySet) {
val addMemberPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.addMemberPolicy)
val removeMemberPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.removeMemberPolicy)
val addAdminPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.addAdminPolicy)
val removeAdminPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.removeAdminPolicy)
val updateGroupNamePolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupNamePolicy)
val updateGroupDescriptionPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupDescriptionPolicy)
val updateGroupImagePolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupImageUrlSquarePolicy)
val updateGroupPinnedFrameUrlPolicy: PermissionOption
get() = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupPinnedFrameUrlPolicy)
data class PermissionPolicySet(
val addMemberPolicy: PermissionOption,
val removeMemberPolicy: PermissionOption,
val addAdminPolicy: PermissionOption,
val removeAdminPolicy: PermissionOption,
val updateGroupNamePolicy: PermissionOption,
val updateGroupDescriptionPolicy: PermissionOption,
val updateGroupImagePolicy: PermissionOption,
val updateGroupPinnedFrameUrlPolicy: PermissionOption,
) {
companion object {
fun toFfiPermissionPolicySet(permissionPolicySet: PermissionPolicySet): FfiPermissionPolicySet {
return FfiPermissionPolicySet(
addMemberPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.addMemberPolicy),
removeMemberPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.removeMemberPolicy),
addAdminPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.addAdminPolicy),
removeAdminPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.removeAdminPolicy),
updateGroupNamePolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupNamePolicy),
updateGroupDescriptionPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupDescriptionPolicy),
updateGroupImageUrlSquarePolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupImagePolicy),
updateGroupPinnedFrameUrlPolicy = PermissionOption.toFfiPermissionPolicy(permissionPolicySet.updateGroupPinnedFrameUrlPolicy)
)
}

fun fromFfiPermissionPolicySet(ffiPermissionPolicySet: FfiPermissionPolicySet): PermissionPolicySet {
return PermissionPolicySet(
addMemberPolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.addMemberPolicy),
removeMemberPolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.removeMemberPolicy),
addAdminPolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.addAdminPolicy),
removeAdminPolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.removeAdminPolicy),
updateGroupNamePolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupNamePolicy),
updateGroupDescriptionPolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupDescriptionPolicy),
updateGroupImagePolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupImageUrlSquarePolicy),
updateGroupPinnedFrameUrlPolicy = PermissionOption.fromFfiPermissionPolicy(ffiPermissionPolicySet.updateGroupPinnedFrameUrlPolicy),
)
}
}
}
Loading

0 comments on commit 1367923

Please sign in to comment.