diff --git a/access-policies.md b/access-policies.md index 2daa8e1..76fe406 100644 --- a/access-policies.md +++ b/access-policies.md @@ -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 @@ -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 | @@ -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 @@ -63,6 +65,51 @@ 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 @@ -70,11 +117,13 @@ 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: diff --git a/auth.md b/auth.md index 54fdb9f..ffff2d0 100644 --- a/auth.md +++ b/auth.md @@ -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 @@ -187,7 +185,7 @@ Logout removes the token from future request headers. ```js - // All future calls will lose the "Authorization" header. + // Resets the "Authorization" header for all future calls. await manifest.logout() ``` diff --git a/crud.md b/crud.md index 56800b4..a547cd8 100644 --- a/crud.md +++ b/crud.md @@ -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, @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/entities.md b/entities.md index ea645f8..b0bf6c8 100644 --- a/entities.md +++ b/entities.md @@ -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 } @@ -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