Skip to content

Commit

Permalink
chore(docs): refine the roles-and-permissions document (#675)
Browse files Browse the repository at this point in the history
Co-authored-by: Nick Gagliardi <[email protected]>
Co-authored-by: Raghd Hamzeh <[email protected]>
  • Loading branch information
3 people committed Mar 18, 2024
1 parent 5c837fa commit bd72dba
Showing 1 changed file with 44 additions and 45 deletions.
89 changes: 44 additions & 45 deletions docs/content/modeling/roles-and-permissions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,19 @@ import {

<DocumentationNotice />

In this guide you will learn how to model roles and permissions model within <ProductName format={ProductNameFormat.ProductLink}/> using the _<ProductConcept section="what-is-an-authorization-model" linkName="authorization model" />_ and _<ProductConcept section="what-is-a-relationship-tuple" linkName="relationship tuple" />_.
To model roles and permissions in <ProductName format={ProductNameFormat.ProductLink}/> using <ProductConcept section="what-is-an-authorization-model" linkName="authorization models" /> and <ProductConcept section="what-is-a-relationship-tuple" linkName="relationship tuples" />. For the purpose of this guide:

- **Roles** are assigned to <ProductConcept section="what-is-a-user" linkName="users" /> or a group of users, where any user can have more than one role (`editor`, `owner`, etc..).
- **Permissions** are what allows users to access certain <ProductConcept section="what-is-an-object" linkName="objects" /> based on their specific roles (`device_renamer`, `channel_archiver`, etc..).
- **Roles** are assigned to <ProductConcept section="what-is-a-user" linkName="users" /> or groups of users, where any user can have more than one role, like `editor` or `owner`.
- **Permissions** allow users to access certain <ProductConcept section="what-is-an-object" linkName="objects" /> based on their specific roles, like `device_renamer` or `channel_archiver`.

For example, the role `viewer` of a `trip` can have **permissions to view bookings** or the role `owners` can have **permissions to add/view bookings to a trip.**
For example, the role `viewer` of a `trip` can have permissions to view bookings, while the role `owners` has permissions to add or view bookings to a trip.

<CardBox title="When to use" appearance="filled">

When trying to create a role and permissions model within <ProductName format={ProductNameFormat.ShortForm}/>.:
When creating a role and permissions model within <ProductName format={ProductNameFormat.ShortForm}/>, you should:

- Create roles by creating relations that can be directly assigned to users
- Assign permissions by creating relations that users get through other relations
- Create roles by creating relations that can be directly assigned to users.
- Assign permissions by creating relations that users get through other relations.

For example:

Expand All @@ -43,20 +43,19 @@ For example:

There are advantages to implementing roles and permissions within <ProductName format={ProductNameFormat.ShortForm}/>, such as:

- Breaking down existing roles to have more fine grained permissions. This allows your application to check whether a _user_ has access to a certain object without having to explicitly check that specific _users_ role.
- Introduce new roles/permissions or consolidate roles without affecting your application behavior. For example: if in your app all the checks are for the fine permissions `check('bob', 'booking_adder', 'trip:Europe')` instead of `check('bob', 'owner', 'trip:Europe')`, and then you later decide `owners` can no longer add bookings to a `trip`, you can remove the relation within the `trip` type with no code changes in your application, and all the permissions will automatically honor the change.
- Breaking down existing roles to have more fine grained permission, which enables your application to check whether a user has access to a certain object without having to explicitly check that specific user's role.
- Introduce new roles/permissions or consolidate roles without affecting your application behavior. For example: if your app's checks are for the fine permissions `check('bob', 'booking_adder', 'trip:Europe')` instead of `check('bob', 'owner', 'trip:Europe')`, and you later decide `owners` can no longer add bookings to a `trip`, you can remove the relation within the `trip` type with no code changes in your application, and all the permissions will automatically honor the change.

</CardBox>

## Before you start

In order to understand this guide correctly you must be familiar with some <ProductConcept /> and know how to develop the things that we will list below.
Familiarize yourself with the <ProductConcept />.

<details>
<summary>

Assume that you have the following <ProductConcept section="what-is-an-authorization-model" linkName="authorization model" />.<br />
You have a <ProductConcept section="what-is-a-type" linkName="type" /> called `trip` that _users_ can be related to as `viewer` and/or an `owner`.
Assume you have the following <ProductConcept section="what-is-an-authorization-model" linkName="authorization model" /><br /> and that you have a <ProductConcept section="what-is-a-type" linkName="type" /> called `trip` that users can be related to as `viewer` and/or as an `owner`.

</summary>

Expand Down Expand Up @@ -90,41 +89,41 @@ You have a <ProductConcept section="what-is-a-type" linkName="type" /> called `t

<hr />

In addition, you will need to know the following:
In addition, you need to know the following:

### Direct Access

You need to know how to create an authorization model and create a relationship tuple to grant a user access to an object. [Learn more ](./direct-access.mdx)
Learn how to create an authorization model and create a relationship tuple to grant a user access to an object. For more information, see [Direct Access.](./direct-access.mdx)

### <ProductName format={ProductNameFormat.ShortForm}/> Concepts

- A <ProductConcept section="what-is-a-type" linkName="Type" />: a class of objects that have similar characteristics
- A <ProductConcept section="what-is-a-user" linkName="User" />: an entity in the system that can be related to an object
- A <ProductConcept section="what-is-a-relation" linkName="Relation" />: is a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
- A <ProductConcept section="what-is-a-relation" linkName="Relation" />: a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
- An <ProductConcept section="what-is-an-object" linkName="Object" />: represents an entity in the system. Users' relationships to it can be define through relationship tuples and the authorization model
- A <ProductConcept section="what-is-a-relationship-tuple" linkName="Relationship Tuple" />: a grouping consisting of a user, a relation and an object stored in Auth <ProductName format={ProductNameFormat.ShortForm}/>
- A <ProductConcept section="what-is-a-relationship-tuple" linkName="Relationship Tuple" />: a group consisting of a user, a relation, and an object stored in Auth <ProductName format={ProductNameFormat.ShortForm}/>
- A <ProductConcept section="what-is-a-relationship" linkName="Relationship" />: <ProductName format={ProductNameFormat.ShortForm}/> will be called to check if there is a relationship between a user and an object, indicating that the access is allowed
- [Direct Relationship Type Restrictions](../configuration-language.mdx#the-direct-relationship-type-restrictions): can be used to indicate direct relationships between users and objects
- A <ProductConcept section="what-is-a-check-request" linkName="Check API Request" /> the Check API Request is used to check for relationships between users and objects
- A <ProductConcept section="what-is-a-check-request" linkName="Check API Request" />: used to check for relationships between users and objects

</details>

<Playground />

## Step By Step

To illustrate modeling Roles and Permissions in <ProductName format={ProductNameFormat.ShortForm}/>, we will use a trip booking system where you can have `owners` and/or `viewers` that can have more granular permissions such as adding bookings to a trip or viewing bookings on it.
To illustrate modeling Roles and Permissions in <ProductName format={ProductNameFormat.ShortForm}/>, imagine a trip booking system that has `owners` and/or `viewers`. It can also have granular permissions like adding bookings or viewing bookings on a trip.

In order to represent this, we need to:
To represent this, you need to:

1. Understand how roles are related to direct relations for our trip booking system
2. Adding implied relations to existing authorization model to define permissions for bookings
3. <ProductConcept section="what-is-a-check-request" linkName="Checking" /> user roles and their permissions based on *relationship
tuples* for direct and implied relations
1. Understand how roles are related to direct relations for the trip booking system
2. Add implied relations to the existing authorization model to define permissions for bookings
3. <ProductConcept section="what-is-a-check-request" linkName="Check" /> user roles and their permissions based on relationship
tuples for direct and implied relations

### 01. Understand How Roles Work Within Our Trip Booking System

Relating roles within <ProductName format={ProductNameFormat.ShortForm}/> can be best described as the following: **Roles are relations that can be directly assigned to users.** Looking at our authorization model, our roles would then be `owner` and `viewer`. Meaning that a specific _user_ can be an `owner` and/or a `viewer`.
In <ProductName format={ProductNameFormat.ShortForm}/>, roles are relations that can be directly assigned to users. In this authorization model, your roles are `owner` and `viewer`, so a specific user can be an `owner` and/or a `viewer`.

<AuthzModelSnippetViewer
configuration={{
Expand Down Expand Up @@ -156,13 +155,13 @@ Relating roles within <ProductName format={ProductNameFormat.ShortForm}/> can be
}}
/>

### 02. Adding Permissions For Bookings
### 02. Add Permissions For Bookings

Permissions within <ProductName format={ProductNameFormat.LongForm}/> can be best described as the following: **Permissions are relations that users get only through other relations.** To represent permissions, we avoid adding a [**direct relationship type restriction**](../configuration-language.mdx#the-direct-relationship-type-restrictions) to the relation in the authorization model. Instead, we define the relation from other relations to indicate that it is a permission granted to and implied from a different relation.
In <ProductName format={ProductNameFormat.LongForm}/>, permissions are relations that users get only through other relations. To represent permissions, define the relation by other relations to indicate that it is a permission both granted to and implied from a different relation. Doing so avoids adding a [direct relationship type restriction](../configuration-language.mdx#the-direct-relationship-type-restrictions) to the relation in the authorization model.

To add permissions related to bookings, we can add new relations to the `trip` _object_ type denoting the various actions a user can take on `trips` (view, edit, delete, rename, etc...)
To add permissions related to bookings, add new relations to the `trip` object type denoting the various actions a user can take on `trips`, like view, edit, or delete.

To allow `viewers` of a `trip` to have **permissions to view bookings** and `owners` to have **permissions to add/view bookings,** we would modify the type as the following:
To allow `viewers` of a `trip` to view bookings and `owners` to add/view bookings, modify the type as seen below:

<AuthzModelSnippetViewer
configuration={{
Expand Down Expand Up @@ -219,16 +218,16 @@ To allow `viewers` of a `trip` to have **permissions to view bookings** and `own
}}
/>

> Note: notice how both `booking_viewer` and `booking_adder` don't have [direct relationship type restrictions](../configuration-language.mdx#the-direct-relationship-type-restrictions). This is to ensure that the relation can only be assigned through the **role** and not directly.
> Note: Both `booking_viewer` and `booking_adder` don't have [direct relationship type restrictions](../configuration-language.mdx#the-direct-relationship-type-restrictions). This ensures that the relation can only be assigned through the role and not directly.
### 03. Checking User Roles And Their Permissions

Now that our type definitions reflects the roles and permissions on how bookings can be viewed/added. Let's create _<ProductConcept section="what-is-a-relationship-tuple" linkName="relationship tuples" />_ to assign roles to _users_ and then *<ProductConcept section="what-is-a-check-request" linkName="check" />* if _users_ have the proper permissions.
Now that your type definitions reflect the roles and permissions governing how bookings can be viewed and added, create <ProductConcept section="what-is-a-relationship-tuple" linkName="relationship tuples" /> to assign roles to users, then *<ProductConcept section="what-is-a-check-request" linkName="check" />* if users have the proper permissions.

Let us create two relationship tuples:
Create two relationship tuples:

1. that gives **bob** the role of `viewer` on `trip`: **Europe**.
2. that gives **alice** the role of `owner` on `trip`: **Europe**.
1. gives `bob` the role of `viewer` on `trip:Europe`.
2. gives `alice` the role of `owner` on `trip:Europe`.

<WriteRequestViewer
relationshipTuples={[
Expand All @@ -247,40 +246,40 @@ Let us create two relationship tuples:
]}
/>

Now we can check: **is bob allowed to view bookings on trip Europe?**
Now check: `is bob allowed to view bookings on trip Europe?`

<CheckRequestViewer user={'user:bob'} relation={'booking_viewer'} object={'trip:Europe'} allowed={true} />

**bob** is a `booking_viewer` because of the following chain of resolution:
`bob` is a `booking_viewer` because of the following chain of resolution:

1. **bob** is a `viewer` on `trip`: **Europe**
2. Any user related to the _object_ `trip:`**Europe** as `viewer` is also related as a `booking_viewer` (i.e `usersRelatedToObjectAs: viewer`)
1. `bob` is a `viewer` on `trip:Europe`
2. Any user related to the object `trip:Europe` as `viewer` is also related as a `booking_viewer` (i.e `usersRelatedToObjectAs: viewer`)
3. Therefore, all `viewers` on a given `trip` are `booking_viewers`

To confirm that **bob is not allowed to add bookings on trip Europe**, we can do the following check:
To confirm that `bob` is not allowed to add bookings on trip `Europe`, perform the following check:

<CheckRequestViewer user={'user:bob'} relation={'booking_adder'} object={'trip:Europe'} allowed={false} />

We can also check: **is alice allowed to view and add bookings on trip Europe?**
You can also check: is `alice` allowed to view and add bookings on trip `Europe`?

<CheckRequestViewer user={'user:alice'} relation={'booking_viewer'} object={'trip:Europe'} allowed={true} />
<CheckRequestViewer user={'user:alice'} relation={'booking_adder'} object={'trip:Europe'} allowed={true} />

**alice** is a `booking_viewer` and `booking_adder` because of the following chain of resolution:
`alice` is a `booking_viewer` and `booking_adder` because of the following chain of resolution:

1. **alice** is a `owner` on `trip`: **Europe**
2. Any user related to the _object_ `trip:`**Europe** as `owner` is also related as a `booking_viewer`
3. Any user related to the _object_ `trip:`**Europe** as `owner` is also related as a `booking_adder`
1. `alice` is a `owner` on `trip:Europe`
2. Any user related to the _object_ `trip:` `Europe` as `owner` is also related as a `booking_viewer`
3. Any user related to the _object_ `trip:` `Europe` as `owner` is also related as a `booking_adder`
4. Therefore, all `owners` on a given `trip` are `booking_viewers` and `booking_adders` on that trip

:::caution
**Note:** Make sure to use unique ids for each object and user within your application domain when creating relationship tuples for <ProductName format={ProductNameFormat.LongForm}/>. We are using first names and simple ids to just illustrate an easy-to-follow example.
Note: Use unique IDs for each object and user within your application domain when creating relationship tuples for <ProductName format={ProductNameFormat.LongForm}/>. This guide uses first names and simple IDs as an easy-to-follow example.
:::

## Related Sections

<RelatedSection
description="Check the following sections for more on how to model for roles and permissions."
description="See following sections for more on how to model for roles and permissions."
relatedLinks={[
{
title: 'Modeling Concepts: Concentric Relationships',
Expand Down

0 comments on commit bd72dba

Please sign in to comment.