# 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:

1. 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.
2. Accessor : Any actor to which privileges can be granted. Within the mitter.io context, all accessors are users.
3. 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 in `channel-id` holding `status` as their Participation status.
4. ACL : An ACL is a list of privilege-selector pairs.
5. 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:

1. ReadMessagePrivilege() `read_message`
2. DeleteMessagePrivilege() `delete_message`

From the section *Basic permissions*, we know that:

1. Any participant of a channel can read a message from the channel.
2. Any authenticated user can delete a message that they sent to a channel.
3. 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:

```
Message(id=msg)
    acls = [+read:participant(chnl), +delete:user(axe), +read:user(axe)]
```

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:

```
Message(id=msg)
    acls = [+read:user(rylai), +read:user(axe), +delete:user(axe)]
```

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:

```
Message(id=msg)
    acls = [-read:user(rylai), +read:participant(chnl), +read:user(axe), +delete(axe)]
```

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:

1. At least one of the accessors that resolve to the user is in the p-list.
2. 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:

1. `+read:user(axe)`, `-read:participant(chnl)` would still result in the `read` privilege not being granted to `axe`
2. 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:

1. Default ACLs : These are the ACLs that are applied to the entity if no ACLs were found on the entity.
2. 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:

1. `user(user-id)` Represents a single user having the provided id.
2. `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.
3. `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 using `acltag(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:

1. `join_channel` Join the channel (add oneself)
2. `add_participant_to_channel` Add any other user as a participant to the channel
3. `list_participants` List the participants of the channel
4. `remove_participant` Remove any other user as a participant to the channel
5. `remove_self` Remove oneself from the channel
6. `delete_messages_from_channel` Delete messages from the channel
7. `read_from_channel` Read the channel object and send messages to it
8. `send_to_channel` Send messages to the channel with themselves as the sender
9. `send_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:

```
Channel(chnl) acls = [
    +add_participant_to_channel:user(admin), +remove_participant:user(admin),
    -join_channel:any_user(), +remove_self:any_user(),
    +read_from_channel:participant(chnl), +send_to_channel:participant(chnl)
]
```

> **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 are `read_message` and `delete_message`.

The default ACLs for a channel are:

1. `+read_from_channel`**:**`participant(channel-id:Active)`
2. `+send_to_channel`**:**`participant(channel-id:Active)`
3. `+list_participants`**:**`participant(channel-id:Active)`
4. `+join_channel`**:**`any_user()`
5. `+remove_self`**:**`any_user()`

The sticky ACLs for a channel are:

1. `+read_from_channel`**:**`user(.system)`
2. `+send_as_other_to_channel`**:**`user(.system)`
3. `+remove_participant`**:**`user(.system)`
4. `+add_participant`**:**`user(.system)`
5. `+list_participants`**:**`user(.system)`
6. `-join_channel`**:**`user(.system)`

#### Message

A Message can have the following privileges assinged to it:

1. `read_message` Read the message
2. `delete_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:

1. `read_message`**:**`participant(channel-id:Active)`
2. `read_message`**:**`user(sender-id)`
3. `delete_message`**:**`user(sender-id)`

The sticky ACLs for a message sent to a channel with id `channel-id` by `sender-id` are:

1. `read_message`**:**`user(.system)`
2. `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:

1. `create_channel` - Create a channel
2. `create_message` - Create a message
3. `create_user` - Create a user
4. `list_channels` - List all the channels in the application
5. `list_user_data` - List the user data for a user (other than oneself)
6. `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:

1. `create_channel`**:**`any_user()`
2. `list_participants`**:**`any_user()`

The sticky ACLs for an application are:

1. `create_channel`**:**`user(.system)`
2. `create_message`**:**`user(.system)`
3. `create_user`**:**`user(.system)`
4. `list_channels`**:**`user(.system)`
5. `list_user_data`**:**`user(.system)`
6. `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:

1. A user can always revoke their own tokens.
2. A user can always request for additional tokens.
3. `.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:

1. PatchType.Set
2. 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:

```
POST /v1/channels
{
    "channelId": "my-channel",
    ... common fields ..,
    "appliedAcls": [
        "-join:any_user()"
    ]
}
```

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:

1. Either we remove the `-join:any_user()` rule from the channel.
2. 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:

```
PATCH /v1/channels/my-channel/acls

{
    "patchType": "Diff",
    "addAcls": [],
    "removeAcls": [
        "-join:any_user()"
    ]
}
```

This will return a response which will contain two channel objects, one before the ACL modification and one after it:

```
{
    "oldEntity": { "channelId": .... },
    "newEntity": { "channelId": .... }
}
```

We can also use a `Set` type operation and instead do:

```
PATCH /v1/channels/my-channel/acls

{
    "patchType": "Set",
    "setAcls": {}
}
```

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).
