Skip to content

Ownership-based access #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 60 additions & 11 deletions access-policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ id: access

Access Policies ensure that we provide specific access to resources for users. You may want to open publicly the **read** access for an entity while limiting **creation** to logged in users for example.

Acces policies are a way to implement **Authorization** following the RBAC (Role-Based Access Control) method. Indeed it is possible to create different entities (ex: User, Manager...) with different access to resources.
Access policies are a way to implement **Authorization** following the RBAC (Role-Based Access Control) method. Indeed it is possible to create different entities (ex: User, Manager...) with different access to resources. You also can limit access to a user own's records using [ownership-based access](#ownership-based-access).

Policies can be added to [entities](./entities.md) or [endpoints](./endpoints.md).
Policies can be added to [entities](./entities.md) and [endpoints](./endpoints.md).

:::info
By default, all CRUD rules access are set to **admin** and thus only available for logged-in admins. Custom endpoints are **public** by default.
:::

## Syntax

Expand All @@ -33,8 +37,6 @@ entities:

In this case, everyone can see the **Invoice** items, only logged-in **Users** can create new ones. Updating an Invoice is restricted to [Admins](./auth.md#admins) only and no one can delete them (not even Admins).

By default, all rules access are set to **admin** and thus only visible by logged-in **Admins**.

| Prop | Description | Type |
| ---------- | ------------------------------------------------------------------------- | ------------------ |
| **access** | The type of access: **public**, **restricted**, **admin**, **forbidden** | AccessType |
Expand All @@ -44,12 +46,12 @@ By default, all rules access are set to **admin** and thus only visible by logge

There are 4 possible access types:

| Access | Description | Short version (emoji) |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| **public** | Everyone has access | 🌐 |
| **restricted** | Only logged-in users have access to it. If _allow_ key specifies one or several entities, users logged in as other entities will not have access. Admins always have access to restricted rules | 🔒 |
| **admin** | Only [admins](./auth.md#admins) have access | 👨🏻‍💻 |
| **forbidden** | No one has access, not even admins | 🚫 |
| Access | Description | Short version (emoji) |
| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| **public** | Everyone has access | 🌐 |
| **restricted** | Only logged-in users have access to it. If _allow_ key specifies one or several entities, users logged in as other entities will not have access. If _condition_ is set to `self`, it limits access to the item owner. Admins always have access to restricted rules | 🔒 |
| **admin** | Only [admins](./auth.md#admins) have access | 👨🏻‍💻 |
| **forbidden** | No one has access, not even admins | 🚫 |

### Entity rules

Expand All @@ -63,18 +65,65 @@ Each entity has **5 rules** where one or several access policies can be applied:

By default, all rules have the [admin access type](#access-types)

## Ownership-based access

Above rules are based on predefined roles, but you many want to grant user access only to their own records. For example, a platform like _Craiglist_ allows its users to create and manage classified ads only for them, not letting users edit others' content.

In Manifest, this is done simply by adding the `{condition: 'self'}` to a restricted policy:

```yaml
User:
properties:
- name
authenticable: true

Project:
properties:
- name
belongsTo:
- User # Should belong to an authenticable entity.
policies:
create:
- { access: restricted, allow: User, condition: self }
```

See ? Just adding the `self` condition prevent creating projects for other users than themselves.

Even if it looks very simple, it has slighlty different impact based on rule where it is added. See the following example:

```yaml
policies:
create:
- { access: restricted, allow: User, condition: self }
read:
- { access: restricted, allow: User, condition: self }
update:
- { access: restricted, allow: User, condition: self }
delete:
- { access: restricted, allow: User, condition: self }
```

This means:

- Create: Record creation is authorized only if owner is the logged in user
- Read: You can only fetch your own record: it forces a filter
- Update: You only can update your own records and cannot change the ownership of them
- Delete: You only can delete your own records

## Additional examples

```yaml
entities:
Project 🗂️:
properties:
- name
belongsTo:
- Manager
policies:
read:
- { access: restricted, allow: [Contributor, Manager] } # Only some entities (and admins).
create:
- { access: restricted, allow: Manager } # Only managers (and admins).
- { access: restricted, allow: Manager, condition: self } # Only managers for themselves (and admins).
update:
- access: 👨🏻‍💻 # Only admin.
delete:
Expand Down
6 changes: 2 additions & 4 deletions auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@ entities:
- name
```

Authenticable entities have 2 extra properties that are used as credentials to log in: `email` and `password`. You do not need to specify them.

The passwords are automatically hashed using _bcryt_ with 10 salt rounds.
Authenticable entities have 2 extra properties that are used as credentials to log in: `email` and `password`. You do not need to specify them.The `email` property expects a unique valid emails and the `password` property is automatically hashed using _bcryt_ with 10 salt rounds.

## Actions

Expand Down Expand Up @@ -187,7 +185,7 @@ Logout removes the token from future request headers.
</TabItem>
<TabItem value="sdk" label="JS SDK" default>
```js
// All future calls will lose the "Authorization" header.
// Resets the "Authorization" header for all future calls.
await manifest.logout()
```

Expand Down
12 changes: 6 additions & 6 deletions crud.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ This operation will create a new item and store it in the database. The newly cr

```json title="Example HTTP Response"
{
"id": '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"name": "Pikachu",
"type": "electric",
"level": 3,
Expand All @@ -280,7 +280,7 @@ This operation will create a new item and store it in the database. The newly cr

console.log(newPokemon);
// Output: {
// id: '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
// id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
// name: "Pikachu",
// type: "electric",
// level: 3
Expand Down Expand Up @@ -314,7 +314,7 @@ Unlike [partial updates](#patch-an-item), this operation will replace the whole

```json title="Example HTTP Response"
{
"id": '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
"id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
"name": "Raichu",
"type": "electric",
"level": 8
Expand All @@ -333,7 +333,7 @@ Unlike [partial updates](#patch-an-item), this operation will replace the whole

console.log(newPokemon);
// Output: {
// id: 'a1b2c3d4-e5f6-4789-abcd-ef0123456789',
// id: "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
// name: "Raichu",
// type: "electric",
// level: 8
Expand Down Expand Up @@ -363,7 +363,7 @@ Unlike [fully replacement](#update-an-item), this operation will only modify the

```json title="Example HTTP Response"
{
"id": 'a1b2c3d4-e5f6-4789-abcd-ef0123456789',
"id": "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
"name": "Pikachu",
"type": "electric",
"level": 5
Expand All @@ -380,7 +380,7 @@ Unlike [fully replacement](#update-an-item), this operation will only modify the

console.log(newPokemon);
// Output: {
// id: 'a1b2c3d4-e5f6-4789-abcd-ef0123456789',
// id: "a1b2c3d4-e5f6-4789-abcd-ef0123456789",
// name: "Pikachu",
// type: "electric",
// level: 5
Expand Down
4 changes: 3 additions & 1 deletion entities.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ Timestamp field (ISO 8601 Format)
```

#### Email
You can create one-to-many relationships or many-to-many relationships. Defining relationships in your entities allows you to load relations when you query them and also filter by relations.


```yaml
- { name: email, type: email }
Expand Down Expand Up @@ -341,7 +343,7 @@ The location type consists in a object with `lat` and `lng` coordinates.

## Relations

You can create **one-to-many** relationships or **many-to-many** relationships.
You can create **one-to-many** relationships or **many-to-many** relationships. Defining relationships in your entities allows you to [load relations](./crud.md#load-relations) when you query them and also [filter your query by relations](./crud.md#filter-by-relation).

### Syntax

Expand Down