Relationship-based access control (ReBAC) is an authorization pattern where permissions are derived from relationships between resources. The most common example of ReBAC is the concept of ownership. For example, the user that created a blog post can edit the blog post because the user is the "owner" of the blog post. However, ReBAC also includes resource hierarchies, user groups, and any other situation where permissions are based on relationships. In this blog post, you will learn how to implement ReBAC in Node.js with Oso Cloud using Oso's GitCloud sample app as an example.
Getting Started With Oso Cloud and Node.js
If you haven't already, create a free Oso Cloud account and set up an API key. Oso Cloud lets you define authorization rules that determine which actors have permissions on a given resource. For the purposes of this tutorial, we will have one type of actor, a User; and two types of resources, a Repository and an Issue. Below is how you can represent these entities in Polar, Oso's declarative authorization language.
Add the above policy to Oso Cloud from the Oso Cloud UI's "Rules" tab.
For this blog post, you'll implement two ReBAC patterns. First, the user that originally created an issue should be able to edit the issue. Second, admin users should be able to edit issues that belong to their repositories. You can represent the ability to edit an issue by adding an edit permission on the Issue resource as follows.
To connect to Oso Cloud from Node.js, install the oso-cloud npm package.
Next, create an Oso Cloud client using your Oso Cloud API key as follows.
The Oso Cloud client has an authorize() function that you can use to check whether a given user has a certain permission on a resource. For example, below is how you can check whether the user Bill has the edit permission on the issue tps-reports-99.
Oso policies contain authorization logic, but you still need authorization data so Oso can know that Bill is an editor on the issue tps-reports-99. In Oso Cloud, authorization data is represented by facts. Below is how you can create a fact using the oso.tell() function.
Now the oso.authorize() call will return true for User Bill and Issue tps-reports-99.
Users Can Edit Their Own Issues
Next, let's update our Oso policy so that users can edit issues that they've created. First, you need to create a relationship between issues and users as follows. The creator relation will store which user created a given issue; and the user who created the issue will automatically get the editor role.
To make this example more concrete, let's say the user Peter created the issue tps-reports-99. Before you set up the relationship that connects the user Peter to the issue tps-reports-99, the oso.authorize() function will return false:
To create the relationship between the user Peter and the issue tps-reports-99, you can call oso.tell() with the has_relation fact type.
With the above fact, Oso Cloud can now resolve that the user Peter can edit the issue tps-reports-99.
Repository Admins Can Edit Issues
Finally, let's update our Oso policy so users that have admin permissions on a particular repository can also edit issues. To do that, first you need to add an admin role to the Repository resource as follows.
Next, you need to add a new relation to issues to track which Repository the issue belongs to. Below is the updated Issue resource definition, with a new repository relation and a new rule that admin role on a repository also grants the editor role on that repository's issues.
In order to show how the new Oso policy works, you need to add two facts to Oso Cloud. First, you need to add a relation that connects the issue tps-reports-99 to the repository tps-reports. Then, you need to grant the user Bill the admin role on the repository tps-reports as follows.
With these two facts, Oso Cloud can now deduce that Bill can edit the issue tps-reports-99, because he has the admin role on the repository tps-reports.
ReBAC to the Future
ReBAC is an authorization pattern that almost every application implements in some way: checking that users can't edit arbitrary data, but can edit their own data is an example of ReBAC. While ownership is the most common ReBAC pattern, there are many other common ReBAC patterns. And ReBAC can be tricky to implement when you have deeply nested data hierarchies. For example, imagine extending this blog post to include making repositories belong to organizations, and allowing organization admins to edit issues. Oso Cloud makes implementing ReBAC much easier, so next time you find yourself implementing a ReBAC pattern from data ownership to data hierarchies, try Oso Cloud first!