# Treetracker permission system

This document is about how to use the auth system to manage users on treetracker, and how to build new authorization policy, and explain how does this work.

## Current Situation (legacy system)

Now we are using self-built auth system, it's based on: user account, role, user-role-mapping, and the role system supports a json-based policy, the policy is composed of permission items, and organization setting.

An example of policy:

```
{
  "policies": [
    {
      "name": "list_stakeholders",
      "description": "Can view stakeholders"
    },
    {
      "name": "manage_stakeholders",
      "description": "Can modify stakeholder information"
    }
  ]
}
```

Another one:

```
{
  "policies": [
    {
      "name": "list_tree",
      "description": "Can view trees"
    },
    {
      "name": "approve_tree",
      "description": "Can approve/reject trees"
    },
    {
      "name": "list_planter",
      "description": "Can view planters"
    },
    {
      "name": "manage_planter",
      "description": "Can modify planter information"
    }
  ],
  "organization": {
    "name": "WRIAddis",
    "id": 774,
    "uuid": "fb341197-143d-4ad5-9b1d-08024d18a46f"
  }
}
```

## New Solution

The new solution is based on Keycloak + ambassador filter, with the Keycloak's resource based authorization system, we can provide a much more powerful permission system, and handle much more complicated permission rules in the future.

With ambassaor, we will add a single auth-service app to handle the permission checking, then all our micro-services will get protected, and we can achieve all these without modifying the current existing micro-services.

### How to use the auth system

This section talks about how to daily maintain the auth system, create user account, assign permission. This is for the reader who wants to JUST maintain the user system.

#### How to create an  admin user (user-manager)

1. Log into Keycloak [https://dev-k8s.treetracker.org/auth](https://dev-k8s.treetracker.org/auth/admin/master/console/#/realms/master/groups)/
2. -> Select `master` realm
3. -> goto `Users`
4. -> click `Add user`
5. -> goto `role mapping`
6. Add `admin` role for this user.
7. Input user info form and submit
8. -> goto `Credential`&#x20;
9. Set password for the user
10. Done, new user can log in and manage the whole system.

#### Assign user roles that  the same as before

Below roles are the same as before, just assign users with these roles then the user would get the corresponding permission.

* Tree Manager (Greenstand operator only)
* Planter Manager (Greenstand operator only)
* Earnings Manager
* Payments Manager
* Comms Manager
* Capture Matcher
* Species Manager
* Stakeholder Manager

To create an user and give them the xxx role.

1. Log into Keycloak [https://dev-k8s.treetracker.org/auth/](https://dev-k8s.treetracker.org/auth/admin/master/console/#/realms/master/groups)
2. -> Select `treetracker` realm
3. -> Select `users`&#x20;
4. -> Click `add user`
5. Fill the user info form and submit
6. -> Click `credental`&#x20;
7. Input the password for the user.
8. -> Click `role mapping`
9. -> Select the `xxx` for the user
10. Done, now the created user is able to log in with all of corresponding permissions.

#### Organization user

Organization users handle the case like giving the Haiti Tree Project (as an organization) the ablity to operate the data (tree, planter) belonging to the Haiti organization.

To create an organization user:

1. Create a normal user the same as above.
2. -> Click `Group`
3. -> Click `new`
4. Input the organization name, e.g. `Tree Boston`
5. -> Click `Attributes`&#x20;
6. Input key `id` and value `n` here, `n` is the id of this organization in the DB
7. -> Click `Add`&#x20;
8. Input key `uuid` and value `n` , here `n` is the uuid, stakeholder id of this organization in DB
9. Back to the user info page that one we just created.
10. -> Click `group`
11. -> Click the organization name we just created
12. -> Click `join`
13. (to assign role) -> Click `role mappings`
14. <mark style="color:red;">-> Select role:</mark> <mark style="color:red;"></mark><mark style="color:red;">`organization-user`</mark>
15. Done, now the user can log in and can visit all those data belongs to that organization.

Note, must choose: `organization-user` as the role, this is the key to give the user organization-spesified behaviors.

### How does it work and advanced useage of auth system

#### Glossary

* **Resource**: the unit of a functionality on treetracker, for example: planter-list
* **Scope**: the action against the resource, e.g. **view**/**edit** the planter list
* **Permission**: resource + scope, e.g. `planter-list:view`
* **Role**: a name, like: `planter manager`
* **Policy**: a rule or condition, like: if the user is a role of `planter manager`

So on the Keycloak, we compose all these concept above to express something like:

If an user is `planter manager` role, then he/her should be able to `view` the `planter-list`

#### Example: earnings manager

This example explains a workflow of adding a new function: earnings tool into the system, and how to create a new resource: `earnings` and give its permission to users, and let the app check if an user has corresponding permission, if the result is false, then they are not able to visit the function, and it includes checking the client and the server-side(micro-service)

1. Create a new resource: `earnings` on Keycloak (ignore the detailed steps)
2. Create scope `edit` if there isn't one.
3. Create role `earnings-manager`
4. Create a user, and assign the role `earnings-manager` to the user.

   Now we are all set in terms of the auth side, but if `earnings` resource is a new function in the system, then we need to add checking point in code to let the app know how to check this resource, we need both client and server-side to be aware of the `earnings` resource and do the check.  These two steps need the developer to do the work.
5. (To add checking into the client, e.g. admin panel) on the menu page, add code similar to below (the real code would be a bit different with this, the user is the object return from Keycloak after logged in):

```
if(user.permissions.includes("earnings:edit"){
  ... //display the earnings menu item
}
```

6\. To let earnings micro-service  check this permission, we need to change the configuration of `auth-service` which is the single point to check all our API requests.

Say, our earnings micro-service is on: <https://prod-k8s.treetracker.org/earnings/>, and we want all the requests targeting to this URL to have the permission `earnings:edit`

6.1 Go to `auth-service` repository, <https://github.com/Greenstand/treetracker-auth>

6.2 Open file: `keycloak.json`

6.3 Add code as below under the `path` property:

```
{
        "name": "earnings",
        "path": "/earnings",
        "methods": [
          {
            "method": "GET",
            "scopes": [
              "edit"
            ]
          },
          {
            "method": "POST",
            "scopes": [
              "edit"
            ]
          }
        ]
      },
```

So here, the `name` is the resource name (earnings), and we will check all traffic matching `/earnings` and check both `get` and `post` havings the permission: `earnings:edit`

7\. Done

#### The case for organization&#x20;

The organization requires a dynamic checking, we need to analyze the request from clients and check if the resource is granted to the user (by access token).

**An example**

The best way to explain it is by an example, this is how do we display tree count planted by an organization:

```
  getCaptureCount(filter) {
    try {
      const query = `${API_ROOT}/api/${getOrganization()}trees/count?where=${JSON.stringify(
        filter.getWhereObj()
      )}`;

      return fetch(query, {
        headers: {
          Authorization: session.token,
        },
      }).then(handleResponse);
    } catch (error) {
      handleError(error);
    }
  },
```

Translate the code above to our auth system:

For API `GET /organizations/:organization_id/trees/count` we need the user to have the permission for a resource (for instance `count-of-tree-in-organization` ) , and the scope/action is `view, and we also need to match the organization id, means, the id in the request should be the same as the id we assigned to this user.`

Let's go through the whole workflow:

* The User opens admin panel and log in by Keycloak
* In the response from Keyclaok, we can get the user info, which includes the property: `organization_id=xxx` (similar as before)
* The user opens the page which displays the count of the organization.
* Request `GET /organizations/xxx/trees/count`
* Ambassador intercept the request and gives it to `auth-service`
* In advance, we should set up the auth-service with config like below:

```
  {
        "name": "count-of-tree-in-organization",
        "path": "/organizations/{organization_id}/trees/count",
        "methods": [
          {
            "method": "GET",
            "scopes": [
              "view"
            ]
          }
        ]
      },
```

Basically, the config above would parse the request and bring the `organization_id` as a claim to Keyclaok.&#x20;

* On Keycloak, the permission: `count-of-tree-in-organization:view` will be attached to a policy, say: `belongs-to-an-organiztion` this policy will use a javascript-based rule to check the permission, which looks like this:

```
var context = $evaluation.getContext();
var identity = context.getIdentity();
var attributes = identity.getAttributes();
var permission = $evaluation.getPermission();

var value = context.getAttributes().getValue("custom");
var attributesMap = attributes.toMap();
var organization_id = attributesMap.organization_id;
if(value.asString(0) === organization_id[0]+""){
    print("custom attr match");
    $evaluation.grant();
}else{
    print("custom attr unmatch");
    $evaluation.deny();
}
```

So this policy will verify if the user's claim (the organization\_id) matches the one set for this user (by assigning the user to a group, as we mentioned above)

* If the check passed, then Keyclaok grants the permission
* The request goes forward to real micro-service.
* The micro-service returns the data to the client.
* Done

## Conclusion

All the explanation above is to demonstrate how to build the auth system, they all are not the final form.

And the whole system has high flexibility and also after the first launch, we can change a lot of things on the auth system to build all kinds of granting rules to fulfill our needs and don't need to change our business code on client and server-side if that change is just about re-organizing our permission rule.

For example, we can control the grain size of the permission, for the earnings, from:

`GET /earnings/*`

to

`Get /earnings/batch/:batch_id`

And also, for the organization tree count example, instead of providing resources like above, the `count-of-tree-in-organzation` (reflect to `GET /organizations/:organization_id/trees/count`, we can use more coarse-grain: `operation-in-organization` to reflect to `GET /organizations/:organization_id/*`
