ACLs and Advanced Permission Model
ACLs (Access Control Lists) allow you to fine tune the permission model to suit the use cases of your applications. ACLs are also resolved and applied on message delivery so ACLs can also be used to manage message routing. The core concepts of the ACL subsystem are:
Privilege : A privilege denotes the ability of an actor to perform a certain operation. An example would be the
read
privilege on messages that allow anyone holding that privilege to read the message.Accessor : Any actor to which privileges can be granted. Within the mitter.io context, all accessors are users.
Accessor selector : A selector denotes a class of accessor. An example of a selector is
participant(channel-id:status)
which denotes the set of all users that are participants inchannel-id
holdingstatus
as their Participation status.ACL : An ACL is a list of privilege-selector pairs.
ACL Entity : Any entity to which ACLs can be applied.
Message
,Channel
,User
,TimelineEvent
are all ACL entities.
Example
To illustrate how ACLs function within mitter.io, let us take an example of a Message
with id msg
sent to a channel with id chnl
by the user axe
. A Message
is an ACL entity to which the following privileges can be applied:
ReadMessagePrivilege()
read_message
DeleteMessagePrivilege()
delete_message
From the section Basic permissions, we know that:
Any participant of a channel can read a message from the channel.
Any authenticated user can delete a message that they sent to a channel.
Any authenticated user can send a message that they sent to a channel.
Both [2] and [3] apply even if the user is no longer a participant on the channel. To implement this, the given message has the following ACLs applied to it:
When we send a message these ACLs are applied by default and this is how we implement the basic permission model that is present even for applications that don't use ACLs.
If axe
now wanted to send a message to the channel that only rylai
could read, he would send the message with his custom ACLs:
Whenever you supply a list of ACLs, the default ACLs are no longer applied. So any behavior you wish to be retained must be reflected by the ACLs you set. When this message is sent, mitter.io will even compute the delivery targets based on these ACLs and the message will be sent only to rylai
s device and only she will see it when she fetches messages via HTTP.
Now take the example where axe
wants to send a message to everyone in the channel except rylai
because rylai
has been acting way too cool recently. To do so, he can set the ACLs as:
When this message is received, mitter.io will not send messages to any of rylais delivery endpoints nor will she be able to fetch it via HTTP calls.
ACL Computation
An ACL is applied over two lists, the p-list
an the m-list
. The p-list
consists of all ACLs that have a +
permission and similarly the m-list
consists of all ACLs that have a -
permission. To check whether a given user has access to a privilege on an entity, we check if:
At least one of the accessors that resolve to the user is in the p-list.
None of the accessors that resolve to the user in the m-list.
Both these conditions need to be true for the ACL to pass as positive and the privilege to be considered granted. This model has certain limitations, but in the current phase of development, this is the model that allows us to maintain performance while still providing feature rich ACLs.
The limitations manifest in certain ways, for instance:
+read:user(axe)
,-read:participant(chnl)
would still result in theread
privilege not being granted toaxe
A empty
p-list
results in no privileges being granted to any user. This however is not permitted in the system due to default ACLs and sticky ACLs kicking in, which is covered further below.
Sticky ACLs and Default ACLs
As we saw in the previous section, .system
has complete access of all the data of the application. Moreoever, certain privileges, like -revoke_tokens_for_self
is a privilege that a user is always granted and this cannot be overriden. To keep these constants in place, there are two ACL lists that are maintainted for each entity:
Default ACLs : These are the ACLs that are applied to the entity if no ACLs were found on the entity.
Sticky ACLs : These ACLs are always implicitly present on the entity and cannot be overriden.
For instance, one of the sticky ACLs on channel is +add_participant:user(.system)
which means that you can always add a participant to any channel using an application key/secret.
Available Accessor Selectors
The following selectors are available:
user(user-id)
Represents a single user having the provided id.participant(channel-id:status)
Represents the group of users which are a participant in the channel with the given channel id, with a particular participation status.any_user()
Represents any authenticated user.
Do note that the ACLs user(.system)
and user(.anonymous)
are forbidden.
COMING SOON Soon, users can be assigned an
aclTag
and all users belonging to an acl tag can be selected usingacltag(tag-name)
.
ACL Entitys and Privilege List
In the following section we will be listing all the ACL entities and the permissions that are available on them. Do note that mitter.io currently only supports setting custom ACLs on messages with support for Channel
and User
coming up in the next release. We will also be releasing partial access to ACLs on Application
via certain configuration options in the subscriber panel.
Channel
A channel can have the following privileges on it:
join_channel
Join the channel (add oneself)add_participant_to_channel
Add any other user as a participant to the channellist_participants
List the participants of the channelremove_participant
Remove any other user as a participant to the channelremove_self
Remove oneself from the channeldelete_messages_from_channel
Delete messages from the channelread_from_channel
Read the channel object and send messages to itsend_to_channel
Send messages to the channel with themselves as the sendersend_as_other_to_channel
Send messages to the channel with anyone else as the sender
For instance, if you wanted to introduce a role called admin
for the channel, who alone can add participants to the channel, but anyone could remove themsleves, you would assign the following privileges to channel:
NOTE The actual accessor selector for a participation selector is
participant(chnl:status)
, but for the sake of brevity we have omitted it. Similarly, the actual privileges on messages areread_message
anddelete_message
.
The default ACLs for a channel are:
+read_from_channel
:participant(channel-id:Active)
+send_to_channel
:participant(channel-id:Active)
+list_participants
:participant(channel-id:Active)
+join_channel
:any_user()
+remove_self
:any_user()
The sticky ACLs for a channel are:
+read_from_channel
:user(.system)
+send_as_other_to_channel
:user(.system)
+remove_participant
:user(.system)
+add_participant
:user(.system)
+list_participants
:user(.system)
-join_channel
:user(.system)
Message
A Message can have the following privileges assinged to it:
read_message
Read the messagedelete_message
Delete the message
These ACLs can be used to selectively send messages to a message from a user that only certain people in the channel can receive. Do note that if a user is assigned an ACL for read for a message sent to a channel, but the user is not a participant in the channel, the user will not be able to read the message unless the user also has the read_from_channel
privilege on the channel the message was sent to.
The default ACLs for a message sent to a channel with id channel-id
by sender-id
are:
read_message
:participant(channel-id:Active)
read_message
:user(sender-id)
delete_message
:user(sender-id)
The sticky ACLs for a message sent to a channel with id channel-id
by sender-id
are:
read_message
:user(.system)
delete_message
:user(.system)
Application
Application ACLs are different only in the sense that they define certain privileges for users at a global level. They contain privileges on the creation of users and channels and control default aspects of the application in general.
NOTE Certain privileges on the application will soon be made available on the user object itself, as the user will be defined as an ACL entity. At that point in time, these privileges will no longer be available on an application.
Access to these ACLs will be made available, but via a controlled manner via the application panel. There is no plan to ever support modifying the direct ACL list of the application.
The following privileges can be assigned to an application:
create_channel
- Create a channelcreate_message
- Create a messagecreate_user
- Create a userlist_channels
- List all the channels in the applicationlist_user_data
- List the user data for a user (other than oneself)write_user_credentials
- Revoke/Issue tokens for a user (other than oneself)
The second permissions is a little special as it only applies for OutOfBand
messages and is currently not applied or in use anywhere. The privilege list_channels
is required to list the channels in an application, but only those channels will be returned for which the user has a read_from_channel
privilege. The subtle difference that manifests is that if the user does not have that privilege on any channel, this call will return an empty list, but if the user does not have list_channels
privilege, it will return a 403 Forbidden
(with a missing_privileges
error code). A user can not have the list_channels
privilege but still send and receive messages to a channel for which they have the appropriate send_to
and read_from
privileges.
The default ACLs for an application are:
create_channel
:any_user()
list_participants
:any_user()
The sticky ACLs for an application are:
create_channel
:user(.system)
create_message
:user(.system)
create_user
:user(.system)
list_channels
:user(.system)
list_user_data
:user(.system)
write_user_credentials
:user(.system)
Do note that despite not having explicit ACLs, the following behavior is ALWAYS true and cannot be overridden in any way:
A user can always revoke their own tokens.
A user can always request for additional tokens.
.system
cannot list user data for itself or issue user tokens for itself.
Fine-grained Control Over ACLs
Introduced in v0.4, you can now not only specify ACLs for an entity up-front, but also modify later according to your specific use-cases.
To modify an ACL, there are two modes that are supported:
PatchType.Set
PatchType.Diff
A PatchType.Set
operation overwrites all ACLs for a given entity as is specified in the payload. A PatchType.Diff
operation on the other hand specifies the specific ACLs that are to be added/removed for an entity.
Let's take an example of a channel that we just created:
What this does is, it creates the channels, adds any participants that were specified in the request, but will not allow any new user to join as -join:any_user()
revokes the join
privilege from every user. Do note that a user can still add some other participant to the channel, since the add_participant
privilege is still available to them. Ideally, we should have revoked both the permissions, but for the sake of brevity we will continue with this example with the single privilege.
Now, after some time, we want to allow users to join the channel. There are two ways of doing this:
Either we remove the
-join:any_user()
rule from the channel.We reset the ACLs to the default state i.e. empty ACLs where the default ACLs for channel will kick-in.
To do this using the first strategy, we will use a Diff
type of ACL modification:
This will return a response which will contain two channel objects, one before the ACL modification and one after it:
We can also use a Set
type operation and instead do:
A Set ACL operation performs a complete set of all ACL records for a given entity. So if we specify an empty object, it will set the entities ACLs to an empty ACL list, which means that for any operations henceforth, it will result in the default ACLs for that entity to kick in.
ACLs for entities can be patched except for Applications. For data-integrity purposes, we currently do not allow any ACLs on Application
to be modified (or specified up-front for that matter).
Last updated