# Branch Permissions

## What are Branch Permissions?

Branch permissions are a way of managing how users may interact with branches when running Doltgres as a server (via `doltgres sql-server`). The branch permissions model is composed of two system tables: `dolt_branch_control` and `dolt_branch_namespace_control`. The former table handles branch modification, while the latter table handles branch creation.

## System Tables

Both system tables—`dolt_branch_control` and `dolt_branch_namespace_control`—have a similar schema. Each column is composed of a string that represents a pattern-matching expression.

### `dolt_branch_control`

| Column      | Type                          | Collation             |
| ----------- | ----------------------------- | --------------------- |
| database    | VARCHAR(16383)                | utf8mb4\_0900\_ai\_ci |
| branch      | VARCHAR(16383)                | utf8mb4\_0900\_ai\_ci |
| user        | VARCHAR(16383)                | utf8mb4\_0900\_bin    |
| host        | VARCHAR(16383)                | utf8mb4\_0900\_ai\_ci |
| permissions | SET('admin', 'write', 'read') | utf8mb4\_0900\_ai\_ci |

### `dolt_branch_namespace_control`

| Column   | Type           | Collation             |
| -------- | -------------- | --------------------- |
| database | VARCHAR(16383) | utf8mb4\_0900\_ai\_ci |
| branch   | VARCHAR(16383) | utf8mb4\_0900\_ai\_ci |
| user     | VARCHAR(16383) | utf8mb4\_0900\_bin    |
| host     | VARCHAR(16383) | utf8mb4\_0900\_ai\_ci |

### Available Permissions

| Permission | Effect                                                                                                                                                                                                                              |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| admin      | Grants all permissions available. In addition, grants write access to entries on both system tables, as long as the database and branch are equivalent, or they're subset of the database + branch that this permission belongs to. |
| write      | Grants the ability to write on the branch, as well as modify the branch in all possible ways (rename, delete, etc.).                                                                                                                |
| read       | Does not explicitly grant any permissions, as having the ability to read a branch is an irrevocable right for all users. This is just for the visual convenience, versus having an empty field.                                     |

Just to reiterate: remember that all users, even those without an entry at all, still have read access to all branches. Permissions only affect modifying branches, and writing to the system tables.

### Pattern Matching

The form of pattern matching that each column implements is very similar to how `LIKE` expressions are parsed. Most characters are interpreted as-is, with the exception of three characters.

| Character | Function                                                                                                                                                                                    |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| \_        | Matches a single character, regardless of what that character is.                                                                                                                           |
| %         | Matches zero or more characters, regardless of what those characters are.                                                                                                                   |
| \\        | Escapes the next character, allowing it to be compared literally. This is only useful when attempting to match one of these special characters, as it is otherwise ignored (has no effect). |

Due to the above rules, it is possible to write two different expressions that have the exact same match characteristics. Rather than allow duplicates, Doltgres processes each expression to find its most concise equivalent (folding the expression). There are two rules that are repeatedly applied to strings until neither applies. The first rule replaces `%%` with `%`. The second rule replaces `%_` with `_%`.

The first rule is very straightforward, as two consecutive "zero or more" characters will still match "zero or more" characters. The second rule simplifies parsing, and gives a definitive ordering to the two characters. Our pattern matcher will create a branch whenever a matching character is found after a "zero or more" character, so ensuring that the "zero or more" character is at the end means that we can reduce at least one branch (that would have come from the "single match" character).

With these two rules, we are able to find matching duplicates and prevent their insertion. These rules also have another side-effect, in that they grant us the ability to determine if a given match expression matches a strict subset of another expression. This is accomplished by using a match expression as the input of another expression, and then checking that the expression's length is smaller. This is used in several ways, such as when creating new branches (to prevent adding unnecessary new entries if an existing entry already encompasses the would-be entry), and also when ensuring that a user with the `admin` permission may modify the correct entries.

## Using the Tables

The branch permissions system tables, `dolt_branch_control` and `dolt_branch_namespace_control`, are both built on top of [users and privileges](https://docs.doltgres.com/reference/server/access-management) system.

### Default State

When the `dolt_branch_control` table is first created, the following statement executes on it: `INSERT INTO dolt_branch_control VALUES ('%', '%', '%', '%', 'write');`. This allows all users to freely modify all branches. They can not, however, modify the system tables.

When the `dolt_branch_namespace_control` table is first created, it is empty. This allows all users to freely create a branch with any name.

### Editing the System Tables

The modification of both system tables are controlled by two entities: the privileges system and the `dolt_branch_control` table itself. Any user that satisfies the privilege requirements may edit the appropriate entries in both system tables.

| Scope        | Privileges                                                         | Remarks                                                                                                                                                 |
| ------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `*.*`        | SUPER, GRANT OPTION                                                | Considered a global admin. May edit any entry in either table, including entries containing special characters.                                         |
| `*.*`        | CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, EXECUTE, GRANT OPTION | Same as above.                                                                                                                                          |
| `database.*` | CREATE, ALTER, DROP, INSERT, UPDATE, DELETE, EXECUTE, GRANT OPTION | Considered a database admin. May edit entries only for their specific database. This also excludes all databases that contain *any* special characters. |

It is important to note that global and database admins do **not** have an implicit permission to modify branches. They must insert their own row if they do not match a pre-existing row on their desired database and branch. This ensures that the table is the single source-of-truth regarding which users may modify branches.

In addition to the above privileges, if a user has the `admin` permission, then that user may edit any entries in either system table that they either directly match, or their database and branch are supersets of the target entries (unless there is a more specific rule, which is further discussed below).

### Branch Modification

All users may read any branch. The branch permissions tables primarily affect modification. When a user (connected via a client to a Doltgres instance running as a server) attempts any operation that may modify either the branch or its contents (tables, staged work, etc.), then the first system table, `dolt_branch_control`, is invoked.

To find all matching entries, we start with the global set of all entries. We first match against the database, which (hopefully) reduces the set of entries. Next we match against the branch, reducing the set further still. We repeat the same process for the user name and host, which gives us our final set. Out of the remaining entries, we filter them down to which entries are the most specific for that branch (e.g. `ab%` vs `abcd`), gather all of the permissions from the filtered set, and then check that the user has the required permissions to execute the desired statement. If not, an error message is thrown.

This same process is also used when attempting to modify the system tables, except that there's an additional check for the user's privileges. If they have the necessary privileges, then we skip the step where we match entries.

### Branch Creation

The `dolt_branch_namespace_control` only applies when a new branch is being created. Any user may freely read from any branch and also create a new branch. They may be restricted in the name that they're able to choose, though.

The process of determining whether a user may create a branch is broken into two sections. In the first, we start with the global set of all entries, and retain all entries that match both the database and branch. If this set is empty, then we **allow** the branch creation. If this set is not empty, then we continue, matching the user and host. If this final set is empty, then we **reject** the branch creation. However, if the final set is not empty, then we allow the branch creation.

After the branch has been created, we add a new entry to `dolt_branch_control` only if that entry would not be a subset of an existing entry with an `admin` permission. This is to reduce duplicate entries as best we can in the table. The new entry will exactly match all of the details of its creator, meaning that no special characters will be used. This entry will also contain the `admin` permission, as the branch creator has full permission to modify their branch, as well as allowing others to modify their branch by adding new relevant entries to the system tables. No entry is added to `dolt_branch_namespace_control`, as branches may not have duplicate names.

### Longest Match

For both `dolt_branch_control` and `dolt_branch_namespace_control`, we only consider the entry set that has the longest matching branch expressions. This is so that the set contains the most specific entries to the target branch and user. This matching occurs *before* the user and host are checked (the database is ignored when looking at the longest match), meaning a longer entry effectively creates a new restricted space. Because we [fold all of our match expressions](#pattern-matching), we can guarantee that all longer matches are a subset of shorter matches. For example, `ab%` is shorter than `abc%`, and consequently `abcd` is a subset of `ab%`. This does leave the case where matches of equivalent length may still be a superset/subset combo (such as `abc_` and `abcd`), however we treat them as having an equivalent specificity.

### Additional Branch Creation Considerations

It is worth noting that operations such as renaming a branch also require the correct permissions on `dolt_branch_control`, or the correct privileges to be an admin. This is because a rename may be viewed as a copy and delete, therefore it is modifying the original branch.

To turn `dolt_branch_namespace_control` into a blacklist (just like `dolt_branch_control`), then run the following statement:

```sql
INSERT INTO dolt_branch_namespace_control VALUES ('%', '%', '', '');
```

This will add an entry that will match every possible branch, but will never match a user as a user will never have an empty user and host. Due to the longest match rule, this entry will be ignored when a valid match is found.

## Storage

All data related to branch permissions is stored in the file `branch_control.db` under the dolt configuration directory (defaults to `.doltcfg`). The rules for selecting a `branch_control.db` file [are the same as for the privileges file](https://docs.doltgres.com/reference/server/access-management).

## Binlog

Currently, all operations are written to a binlog that is stored alongside the system tables. There are no ways of accessing the binlog for now, but in the future support will be added. The binlog is intended for correcting mistakes, as well as providing history on how the system tables have changed.

## Examples

The following examples are a small snippet that shows branch permissions in action. It is assumed that this document has been read in full, as concepts that are explained in previous sections are not explained in-depth. The [setup section](#setup) is run before each example, therefore remember to have a dedicated terminal window for [setup](#setup) if you want to try any of these examples yourself. All examples other than [setup](#setup) will exclusively use the second terminal window. All examples will use Postgres' default client, `psql`. Feel free to use your desired Postgres client.

### Setup

This handles the creation of the directory, along with starting the server. As we are running these examples locally, we will use two terminal windows. The first window will contain the server, which we are starting here. It is worth noting that the examples use the default host and port of `localhost:3306`. If these are already in use for your system, then you may supply the arguments `--host="<your_host_here>"` and `--port=<your_port_here>` to change them.

As explained in the [default state section](#default-state), we automatically add a row to the `dolt_branch_control` table that allows all users to modify all branches by default. For these examples, we remove that default row. This allows us to demonstrate the desired functionality a bit more easily.

By supplying the argument `--user=root`, we add a password-less user named `root` that has every global privilege. This makes it very easy to get your database set up, and we will be taking advantage of this user in these examples. We also, however, want a user that does *not* have every privilege, and is more representative of a standard user. We name that user `testuser` in the examples. Although it appears that we grant them every global privilege, this is not the case, as they are still missing the `GRANT OPTION` privilege. This means that they are not considered an admin, however we do not have to worry about assigning privileges to allow for basic table operations. [You may read more about this behavior in an earlier section.](#editing-the-system-tables)

```sql
DELETE FROM dolt_branch_control;
CREATE USER testuser@localhost;
GRANT ALL ON *.* TO testuser@localhost;
```

### The `write` Permission

Please refer to the [setup section](#setup) before continuing this example. This example shows the `write` permission in action. We add the `write` permission to the `testuser` user, which allows that user to modify the contents of our `main` (default) branch, while the `root` user does not have the permission and cannot make any modifications.

```sql
$ psql --user=root
INSERT INTO dolt_branch_control VALUES ('%', 'main', 'testuser', '%', 'write');
CREATE TABLE test (pk BIGINT PRIMARY KEY);
Error 1105: `root`@`%` does not have the correct permissions on branch `main`

$ psql --user=testuser
CREATE TABLE test (pk BIGINT PRIMARY KEY);
```

### The `admin` Permission

Please refer to the [setup section](#setup) before continuing this example. This example shows the `admin` permission in action. `admin` functions similarly to the `write` permission, however it also allows the user to `INSERT`/`UPDATE`/`DELETE` entries that match the database and branch (or the resulting set of potential matches from the database and branch match expressions are a subset of the one that contains the `admin` permission). We show this by demonstrating that `testuser` cannot modify the `main` (default) branch at first, nor can they modify the `dolt_branch_control` and `dolt_branch_namespace_control` tables. We then switch to `root` (who is a [global admin](#editing-the-system-tables)) and give `testuser` an `admin` permission over the `main%` branch. The [special "zero or more" character](#pattern-matching) means that they may add additional entries of `main`, as well as other branches that begin with `main`. The users added are all fake, and are just used to demonstrate the capability. We end by showing that this only applies to the exact match expression, as the very similar `_main` branch name is still off-limits.

```sql
$ psql --user=testuser
CREATE TABLE test (pk BIGINT PRIMARY KEY);
Error 1105: `testuser`@`localhost` does not have the correct permissions on branch `main`
INSERT INTO dolt_branch_control VALUES ('example', 'main', 'newuser', '%', 'write');
Error 1105: `testuser`@`localhost` cannot add the row ["example", "main", "newuser", "%", "write"]
INSERT INTO dolt_branch_namespace_control VALUES ('example', 'main', 'newuser', '%');
Error 1105: `testuser`@`localhost` cannot add the row ["example", "main", "newuser", "%"]

$ psql --user=root
INSERT INTO dolt_branch_control VALUES ('example', 'main%', 'testuser', '%', 'admin');

$ psql --user=testuser
CREATE TABLE test (pk BIGINT PRIMARY KEY);
INSERT INTO dolt_branch_control VALUES ('example', 'main', 'newuser', '%', 'write');
INSERT INTO dolt_branch_control VALUES ('example', 'main_new', 'otheruser', '%', 'write');
INSERT INTO dolt_branch_control VALUES ('example', '_main', 'someuser', '%', 'write');

INSERT INTO dolt_branch_namespace_control VALUES ('example', 'main1', 'theuser', '%');
INSERT INTO dolt_branch_namespace_control VALUES ('example', '_main', 'anotheruser', '%');
```

### Restricting Branch Names

Please refer to the [setup section](#setup) before continuing this example. This example shows how to [restrict which users are able to use branch names](#branch-creation) with the `main` prefix. To do this, we insert a `main%` entry into the `dolt_branch_namespace_control` table, assigning the `testuser` user. We create another entry with `mainroot%` as the branch name, and assign that to `root`. This means that `root` is able to create any branches with names that **do not** start with `main`, but is unable to create branches that **do** start with `main`. The exception being `mainroot`, which while having `main` as a prefix, it is considered [the longest match](#longest-match), and therefore takes precedence over the `main%` entry. Consequently, `testuser` cannot use `mainroot` as a prefix, as [the longest match](#longest-match) overrides their `main%` entry.

```sql
USE example;
INSERT INTO dolt_branch_namespace_control VALUES ('%', 'main%', 'testuser', '%');
INSERT INTO dolt_branch_namespace_control VALUES ('%', 'mainroot%', 'root', '%');
SELECT DOLT_BRANCH('does_not_start_with_main');
+--------+
| status |
+--------+
| 0      |
+--------+

SELECT DOLT_BRANCH('main1');
Error 1105: `root`@`%` cannot create a branch named `main1`
SELECT DOLT_BRANCH('mainroot');
+--------+
| status |
+--------+
| 0      |
+--------+
1 row in set (0.00 sec)

example=# exit;

$ psql --user=testuser
SELECT DOLT_BRANCH('main1');
+--------+
| status |
+--------+
| 0      |
+--------+
1 row in set (0.00 sec)

SELECT DOLT_BRANCH('mainroot1');
Error 1105: `testuser`@`localhost` cannot create a branch named `mainroot1`
```

### Multiple Databases

Please refer to the [setup section](#setup) before continuing this example. This example simply shows how an entry in each system table is scoped to a database. Our pre-existing database is `example`, as Doltgres uses the directory's name for its database name. Therefore, we create another database named `newdb`, which the user `root` will not have any permissions on.

```
psql --user=root
CREATE TABLE test (pk BIGINT PRIMARY KEY);
Error 1105: `root`@`%` does not have the correct permissions on branch `main`
INSERT INTO dolt_branch_control VALUES ('example', '%', 'root', '%', 'write');
CREATE TABLE test (pk BIGINT PRIMARY KEY);
DROP TABLE test;
CREATE DATABASE newdb;
USE newdb;
CREATE TABLE test2 (pk BIGINT PRIMARY KEY);
```
