Selective Deliveries

Mitter provides Access Control Lists (ACLs) out of the box to let you choose whom to deliver a message to in a group channel. Let’s add a little function to our app to see this in action.

Login as different users

We created multiple users for our app through the Mitter.io Dashboard at the beginning of this guide. If you haven’t already, take some time to create 3 or more users and copy down their IDs and auth tokens.

Now, you can obviously put up a login page to your app and use a backend server as a token server to login as a user; but for the sake of simplicity, we’ll just swap out the user credentials before building our app.

Get started by defining 3 UserAuth objects inside the onCreate() method in your MyApp class like this:

MyApp.kt
val jasonAuth = UserAuth(
    userId = "8ed92f3c-0696-4513-a842-085e3cee589e",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IlJkZHZCNXJ5RGdPNUpvSkoiLCJ1c2VydG9rZW4iOiI5cXA1MjQyY2N1NW9lbHI3OTRzc2ltbDY3OCJ9.aonmZhZWCyIHJR6nxlNn_KSgAvdWlB4vtZgfdXbvlIvXBM5oNaUzpF3YbfAlZeyPr8_uMf8HCcoh4dVFr-lYFw"
)

val katieAuth = UserAuth(
    userId = "cb02bc00-979e-4db2-8625-116178c4ad95",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6Ilg2ZXZKZDVHSmx1SU5URWEiLCJ1c2VydG9rZW4iOiJwdnA2MGFwa3NpYjgzY21yOGI1N2g0YmhpYSJ9.hVw0h3hmtOQlx9phWrJ7zK9an9GmXv9H481OjrbIPOmE58g86EqozHLhASwl0jdiqC5KjU1_nrIK40pmNEvY9w"
)

val samAuth = UserAuth(
    userId = "5cfe3da1-4467-49b8-8325-3e85cec31c5a",
    userAuthToken = "eyJhbGciOiJIUzUxMiJ9.eyJpc3MiOiJtaXR0ZXItaW8iLCJ1c2VyVG9rZW5JZCI6IlhoSnV5dGw0OG5OSlBJVDYiLCJ1c2VydG9rZW4iOiIyamozaTdhNTA5Yzc3c2Vva2dscGdiZjBpNiJ9.BwPGV2kTdclHdRaIzqjR6yAascqvYE52tH2iLK2aDBaBZA841SM0gW0WdblxLYeAyyM-XKXIEHwM2K0xQwoh7w"
)

Next, fire up 3 emulator instances or 3 devices, whichever you prefer, each time swapping out the userAuth parameter on the Mitter object configuration defined in your Application class. Like this:

MyApp.kt
mitter = Mitter(
    context = this,
    mitterConfig = mitterConfig,
    userAuth = samAuth
)

Therefore, first, you can pass userAuth as jasonAuth and run the project on one emulator instance. Then you can change the userAuth to katieAuth and then run on a different emulator instance.

This way you can get 3 different users in your 3 emulator instances, and then you can chat between the 3 in real-time.

Implement the @username mapping

For our demo, we’ll be adding a feature where a user can mention another specific user by the @username notation while typing their messages, and the message will only be visible to the mentioned user and the sender.

Before we can get started on that, we need to have a local mapping of the usernames with their actual user IDs on the platform.

Define an object class (or a class with static methods, if using Java) named UserIdProvider and add the following piece of code, changing the user IDs and names to your actual user details.

UserIdProvider.kt
object UserIdProvider {
    fun getUserId(username: String): String = when (username.toLowerCase()) {
        "sam" -> "5cfe3da1-4467-49b8-8325-3e85cec31c5a"
        "jason" -> "8ed92f3c-0696-4513-a842-085e3cee589e"
        "katie" -> "cb02bc00-979e-4db2-8625-116178c4ad95"
        else -> ""
    }
}

Do note that these usernames are just local names that you can use to identify a user with the @username notation.

Add ACL rules

Now that you’ve defined the mapping, you need to define some ACL rules that you’ll be passing along with your messages to make the selective delivery work.

Create another object class (or a plain class with static method, if using Java) called AclUtils and add the following piece of code:

AclUtils.kt
object AclUtils {
    fun meAndSam(senderId: String): AppliedAclList {
        return AppliedAclList(
            plusAppliedAcls = listOf(
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("sam")))
                ),
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(senderId))
                )
            ),
            minusAppliedAcls = emptyList()
        )
    }

    fun meAndJason(senderId: String): AppliedAclList {
        return AppliedAclList(
            plusAppliedAcls = listOf(
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("jason")))
                ),
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(senderId))
                )
            ),
            minusAppliedAcls = emptyList()
        )
    }

    fun meAndKatie(senderId: String): AppliedAclList {
        return AppliedAclList(
            plusAppliedAcls = listOf(
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(UserIdProvider.getUserId("katie")))
                ),
                AppliedAcl
                (
                    ReadMessagePrivilege(),
                    UserIdAccessorSelector(IdUtils.of(senderId))
                )
            ),
            minusAppliedAcls = emptyList()
        )
    }

    fun getAclListFromUsername(username: String, senderId: String) = when (username) {
        "sam" -> meAndSam(senderId)
        "jason" -> meAndJason(senderId)
        "katie" -> meAndKatie(senderId)
        else -> emptyAclList()
    }
}

Here, we just define some basic ACL rules based on the chosen username. You can learn more about ACLs and how to use them over here.

For example, the method meAndSam() provides an AppliedAclList object which tells Mitter.io that the message is only for Sam and the current sender to view. Anyone else in the group will be unaware of this message.

This way you can have a private conversation between users in a group of multiple users.

Apply ACLs to your outgoing messages

The sendTextMessage() method that we used previously to send out messages, accepts another optional parameter called appliedAcls where you can pass an AppliedAclList object containing your ACL rules for the message you’re sending.

The goal here is to parse the input text and check if there’s an @ symbol present in the message and then extract the username from that message to determine our ACLs.

We’ll be using our utility methods to determine the ACLs for the username that we get from the input text.

Modify your button click event to this:

MainActivity.kt
sendButton.setOnClickListener {
    var typedInput = inputMessage?.text.toString()
    var appliedAcls = emptyAclList()

    if (typedInput.contains('@')) {
        val username = typedInput.substring(1, typedInput.indexOf(' '))
        typedInput = typedInput.substringAfter(' ')
        appliedAcls = AclUtils.getAclListFromUsername(username, mitter.getUserId())
    }

    messaging.sendTextMessage(
        channelId = channelId,
        message = typedInput,
        appliedAcls = appliedAcls,
        onValueUpdatedCallback = object : Mitter.OnValueUpdatedCallback {
            override fun onError(apiError: ApiError) {}

            override fun onSuccess() {
                inputMessage?.text?.clear()
            }
        }
    )
}

Here, we just do a basic check for the @ symbol, substring the text accordingly, and pass the message along with the applied ACLs to the sendTextMessage() method.

Now, if you run the app on all the 3 emulator instances with 3 different users, you can chat among the users and send private messages by just mentioning a user’s name prefixed with an @ symbol.

For example, if Katie would like to send a message to just Jason, she can type “@jason What’s up?”.

This will make the message visible only to Katie and Jason. Sam will be totally unaware that this conversation ever happened.

That’s how you can use ACLs to control who sees what. Feel free to define your own use cases and play around.

Last updated