Hasura Authorization System through Examples
Published: 2020-03-24 10:12:17

---
title: Hasura Authorization System through Examples
published: true
date: 2020-03-16 23:04:15 UTC
tags:   
canonical_url: https://hasura.io/blog/hasura-authorization-system-through-examples/
---

![](https://hasura.io/blog/content/images/2020/03/Hasura-authz-by-example.png)

Authorization systems are about being able to specify rules that will allow the system to answer, “Can user U perform action A [on resource R]?”. In this post, we look at implementing some real-world authorization rules using Hasura's JSON-based DSL. But first, a quick recap of Hasura's authorization system.

<!--kg-card-begin: html-->
## Authorization with Hasura: Recap
<!--kg-card-end: html-->

Every request to Hasura executes against a set of session variables. These variables are expected to be set by the authentication system. While arbitrary variables can be set and used in defining authorization rules, two variables are of particular interest in the context of authorization:

- `X-Hasura-User-Id`: This variable usually denotes the user executing the request.
- `X-Hasura-Role`: This variable denotes the role with which the user is executing the current request. Hasura has a built-in notion of a role and will explicitly look for this variable to infer the role.

We can create new roles—which are just arbitrary names—from the Hasura console. Once a role is created, we can set permissions for `select`, `insert`, `update`, and `delete` **for each table** in our schema. A permission rule is a JSON object that looks something like this:

```
{"user_id": {"_eq": "X-Hasura-User-Id"}}
```

The above rule is equivalent to saying: allow this request to execute on the current row if the value of the `user_id` column equals the value of the session variable `X-Hasura-User-Id`.

The JSON here is essentially a domain language for expressing authorization rules. It is possible to express pretty complex rules:

- Any columns in the table can be used in place of `user_id`
- Several operators ( `_gt`, `_lt`, `_in`, etc) can be used in place of `_eq` operator
- It is possible to traverse relationships (we will see how to do this shortly)
- Operators `_and` and `_or` can be used to chain rules
- It is even possible to query tables not related to the current object as part of the rule execution (again, more on this shortly).

All of this is documented in the official [documentation](https://hasura.io/docs/1.0/graphql/manual/auth/authorization/index.html). Let us now look at how to apply these in the real world.

<!--kg-card-begin: html-->
## Example 1: Access control for a payroll management system

#### Simple role-based access control
<!--kg-card-end: html-->

Consider a simple HRMS system with the roles HR, Employee, Manager, and Director. We want to implement the following authorization rules:

- Only HR should be able to edit payrolls
- Employees should be able to see their own payrolls
- Managers should be able to view all payrolls, but not edit them

The database schema and relationships for this use case would look like:

![](https://hasura.io/blog/content/images/2020/03/Screenshot-from-2020-03-15-17-07-09.png)

Once the above schema and relationships have been created in Hasura, we can create the custom roles HR, Employee, and Manager. We then need to create the following permission rules on the payroll table for each of these roles:

<!--kg-card-begin: markdown-->
- HR:
  - Select: Without any checks
  - Insert: Without any checks
  - Update: Without any checks
  - Delete: Without any checks
- Employee:
  - Select: `{ "employee_id": {"_eq": "X-Hasura-User-Id"} }`
  - Insert: Denied
  - Update: Denied
  - Delete: Denied
- Manager:
  - Select: Without any checks
  - Insert: Denied
  - Update: Denied
  - Delete: Denied
<!--kg-card-end: markdown-->

Now suppose we want a manager to only be able to read or update their reportees' payrolls (but not payrolls of other employees). Assuming relationships "employee" from payroll to employee (using `employee_id`), and "manager" from employee to employee (using `manager_id`), we need to change the `select` and `update` rules for the role `manager` on the `payrolls` table to be:

```
{ 
    "employee": {
    	"manager_id": {"_eq": "X-Hasura-User-Id"}
    }
}
```

The above rule tells Hasura to fetch the employee associated with the payroll record and match their `manager_id` with the current user id. Hasura can traverse arbitrarily nested relationships, making this a powerful construct. In addition, we need to set the column update permissions so that only the salary field can be updated:

![](https://hasura.io/blog/content/images/2020/03/Screenshot-from-2020-03-16-12-45-19.png)

<!--kg-card-begin: html-->
## Example 2: Per-document roles in Google Docs

#### Per-resource roles
<!--kg-card-end: html-->

Lets us a look at a more complicated example: a Google Docs clone where we have documents, users, and roles per document. Each document can have owners, editors, and viewers with these rules:

- Owners can read, update and delete a document
- Editors can read and update a document
- Viewers can only read a document
- Other users can't read, update or delete a document

In this case, we can use a single Hasura role `user` and directly model the per document role in the schema. The database schema and relationships would look something like this:

![](https://hasura.io/blog/content/images/2020/03/Screenshot-from-2020-03-15-17-07-31.png)

After creating the above schema and relationships in Hasura, we can configure the following permission rules on the `documents` table for the Hasura role `user`:

**Select permissions:**

```
{
    "_or": [
        {"owners": {"user_id": {"_eq": "X-Hasura-User-Id"}}},
        {"editors": {"user_id": {"_eq": "X-Hasura-User-Id"}}},
        {"viewers": {"user_id": {"_eq": "X-Hasura-User-Id"}}}
    ]
}
 
```

This rule illustrates the use of the `_or` operator. The rule will apply if any of the three conditions in the array are matched.

**Update permissions** :

```
{
    "_or": [
        {"owners": {"user_id": {"_eq": "X-Hasura-User-Id"}}},
        {"editors": {"user_id": {"_eq": "X-Hasura-User-Id"}}}
    ]
}
 
```

**Delete permissions:**

```
{"owners": {"user_id": {"_eq": "X-Hasura-User-Id"}}}}
```

**Insert permissions:**

Any user can insert a document. However, whenever a document is created, we need to make sure that the `owners` table is also populated along with the `(user_id, document_id)` tuple so that `select`s, `update`s and `delete`s don't fail. This can be done by setting up a Postgres event trigger:

```
-- Create the function
CREATE OR REPLACE FUNCTION insert_owner_for_document ()
  RETURNS TRIGGER
  AS $BODY$
DECLARE
  session_variables json;
BEGIN
  session_variables := current_setting('hasura.user', 't');
  INSERT INTO owners (document_id, user_id)
    VALUES (NEW.id, (session_variables->>'x-hasura-user-id')::INTEGER);
  RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;

CREATE TRIGGER trigger_insert_owner
  AFTER INSERT ON documents
  FOR EACH ROW
  EXECUTE PROCEDURE insert_owner_for_document ()
```

**Note** : In this example, there are two role-based systems in action: Hasura's built-in role-based access control system, and the per-document role-based access control system implemented using the above database schema and permission rules. As far as Hasura's system is concerned, all requests are being executed against the `user` role.

<!--kg-card-begin: html-->
## Example 3: Hierarchical roles in an organization

#### Hierarchical roles
<!--kg-card-end: html-->

In a hierarchical role-based access control system, roles are arranged into a hierarchy and a user with a role will have access to anything that the role or a child role has access to.

**Example** : Consider an organization with the following role hierarchy: employee > lead > manager > director > department\_head. A user who is higher in the hierarchy should be able to do any action that someone lower in the hierarchy can do. For example, a manager should be able to do anything a lead or an employee can do. Another way of looking at this is that the manager is assigned multiple roles [`manager`, `lead`, `engineer`], and they have access to a resource if at least one of these roles has access to it.

For this example, we will again use a single Hasura role `user` and directly model the roles in the schema:

![](https://hasura.io/blog/content/images/2020/03/Screenshot-from-2020-03-16-15-49-19.png)

We will also assume there is a `documents` table that only managers should have access to.

For matching a user against all applicable roles in the hierarchy, we need to recursively traverse the role table to fetch all the roles. While Hasura's DSL allows us to traverse relationships, we cannot do so recursively. So we have to figure out a way to flatten the role hierarchy. Assume we have magically created the following view:

- `flattened_user_roles (user_id, role_id)`: This will contain the result of traversing the role hierarchy and creating all possible `(user_id, role_id)` combinations. 

We can now write the permission rule for `select`s on the `documents` table:

```
{
    "_exists": {
        "_table": {
           "table": "flattened_user_roles",
           "schema": "public"
        },
        "_where": {
            "_and": [
                { "user_id": {"_eq": "X-Hasura-User-Id"} },
                { "role_id": {"_eq": "manager"} }
            ]
        }
    }
}
```

The above rule illustrates the use of the `_exists` operator. `_exists` allows us to write rules that query tables unrelated to the row being accessed. The rule succeeds if the conditions in the `_where` clause yield at least one row.

How do we generate `flattened_user_roles` though? Postgres' `WITH RECURSIVE` to the rescue!

```
CREATE OR REPLACE VIEW flattened_user_roles AS
WITH RECURSIVE sub_tree AS (
  SELECT
    roles.id AS role_id,
    user_roles.user_id AS user_id
  FROM
    user_roles,
    roles
  WHERE
    roles.id = user_roles.role_id
  UNION ALL
  SELECT
    r.id AS role_id,
    st.user_id AS user_id
  FROM
    roles r,
    sub_tree st
  WHERE
    r.parent_role_id = st.role_id
)
SELECT
  *
FROM
  sub_tree;
```

In the above query, we create a view that generates a flattened version of the hierarchical `user_roles` table.

**Exercise:** How would you implement a scenario with per-resource role-based access control that is also hierarchical? Hint: Instead of using `_exists` above, traverse the relationship from the object.

**Note:** This example also shows how to model scenarios where one user can have multiple roles. Hasura's built-in role-based access control system currently allows a request to only be executed against a single role. However, by modeling the roles directly in the database, we can implement scenarios where one user has multiple roles. We are also working on adding [multiple role support](https://github.com/tirumaraiselvan/graphql-engine/blob/multiple-roles-reads/rfcs/multiple-roles-reads.md) directly to Hasura.

<!--kg-card-begin: html-->
## Example 4: Attribute-based access control for a CMS

#### Attribute-based access control
<!--kg-card-end: html-->

Attribute-based access control is about being able to write authorization rules based on the attributes of a resource.

**Example** : Consider a university with multiple departments, and each department has documents and users. We want users to be able to read documents that belong to their department. The schema in this case would look something like:

![](https://hasura.io/blog/content/images/2020/03/Screenshot-from-2020-03-15-17-09-19.png)

Assuming there is a department relationship defined from document to department, and a users relationship defined from department to users, the `select` permissions on the `documents` table would be:

```
{
    "department": {
    	"users" : {"id": {"_eq": "X-Hasura-User-Id"}}
    }
}
```
<!--kg-card-begin: html-->
## Conclusion
<!--kg-card-end: html-->

In this post, we looked at implementing authorization rules for several real-world use cases. Along the way, we learned the syntax for writing some pretty complex rules. The example under "Hierarchical roles in an organization" illustrated using a view to handle more complex scenarios. If you would like to learn more:

- The [learn course](https://hasura.io/learn/graphql/hasura-auth-slack/introduction/) has a tutorial on building permission rules for a Slack clone. 
- The [documentation](https://hasura.io/docs/1.0/graphql/manual/auth/authorization/permission-rules.html) explains everything in great detail.
- [This blog post](https://hasura.io/blog/access-control-patterns-with-hasura-graphql-engine/) explains how to build authorization rules for a Hacker News clone.

If you are using Hasura and need help with authorization, or want to share some interesting use cases you have implemented, tweet to us at [@HasuraHQ](https://twitter.com/hasurahq) or join us on [Discord](discord.gg/hasura) for more discussions on Hasura & GraphQL! 

Sign up for our [newsletter](http://eepurl.com/dBUfJ5) to know when we publish new articles.