Selective Deliveries

Last updated 2 months ago

As our first introduction to ACLs, in this section, we will employ ACLs on our messages to perform selective deliveries.

Introducing ACLs

ACLs are a way to define fine-grained access to entities within Mitter.io. You can define which user (or class of users) can or cannot perform an action. ACLs can also be modified during the lifetime of an entity (except for Messages) to introduce extremely rich behavior for your apps.

In this example, what we will do is allow users to send private messages within a group channel. Whenever a user types a message starting with @username, it will send out a message on the group, but ONLY to that particular user.

An ACL is made up of two lists: the p-list (or the plusAppliedList) and the m-list (or the minusAppliedList). A given user (also called as an accessor) can perform an action, if for the action the accessor is a part of at least one selector in the plusAppliedList and is a part of no selector in the minusAppliedList. For example, if @john were to send a message only to @candice, his message would have the following ACL:

appliedAcls: {
plusAppliedAcls: ["read_message:user(@candice)"],
minusAppliedAcls: []
}

On the other hand, if @candice wanted to send a message to everyone except @john, the ACLs on her message would look like:

appliedAcls: {
plusAppliedAcls: ["read_message:any_user()"],
minusAppliedAcls: ["read_message:user(@john)"]
}

Allowing mentions in your messages

We need to now allow users to mention specific users when sending messages and attach the corresponding ACLs. To do this, open up ChatComponent.ts and modify the sendMessage function:

ChannelComponent.js
sendMessage() {
const mitter = this.props.mitter
const messageToSend = this.state.typedMessage
const privateMessagePattern = /^(@[a-zA-Z0-9]+)/ // [1]
const privateMessageMatch = messageToSend.match(privateMessagePattern)
let appliedAcls = null
if (privateMessageMatch !== null) {
appliedAcls = {
plusAppliedAcls: [ // [2]
`read_message:user(${mitter.me().identifier})`,
`read_message:user(${privateMessageMatch[0]})`
]
}
}
this.setState((prevState) => Object.assign({}, prevState, {
typedMessage: ''
}))
this.messageInput.focus()
mitter.clients().messages()
.sendMessage(this.state.activeChannel, {
senderId: mitter.me(),
textPayload: this.state.typedMessage,
timelineEvents: [
{
type: "mitter.mtet.SentTime",
eventTimeMs: new Date().getTime(),
subject: mitter.me()
}
],
appliedAcls: appliedAcls // [3]
})
}

Everything is the same as the previous function, so let's discuss what we have changed so far:

  1. We are using a pattern to match the beginning of a string starting immediately with @ followed by a sequence of alphanumeric characters. This will match any string that starts with a pattern like @john, @candice etc.

  2. If we find that the pattern matches, we construct the ACL object. If no such pattern is found, the ACL object stays as null. There are two things to note here. First, we specify the identifiers in an ACL string directly; they are not nested inside any identifier object. This is because Mitter.io ACLs use a custom syntax and are not JSON objects. Second, we have added a read privilege for the sending user as well. When you do not use ACLs, Mitter.io attaches ACLs that allow the user and all the participants in a channel to read the message; but if you decide to use ACLs, then Mitter.io does not perform an operation on the incoming ACLs. So if you did not provide a read message privilege for the user, that message would not be delivered even to the sender.

  3. Finally we attach the ACLs to the message object.

Now open up three browser windows pointing to http://localhost:3000/user/@john , http://localhost:3000/user/@amy and http://localhost:3000/user/@candice. Point to the #roadtrip channel in all three of them. Try sending out message to each other and mentioning a specific user and see what happens. Here's a quick demo of this app in action:

A demo app showing private replies within a group chat

We would also like to render such private messages differently. Maybe, send them as non-text messages itself? In the next section, we will be looking at exploring custom payloads and an introduction to some Mitter helper components.