From 765035fa32253b3ff4f9839cc58d50b33bbda496 Mon Sep 17 00:00:00 2001 From: ebarault Date: Wed, 1 Mar 2017 02:06:37 +0100 Subject: [PATCH 1/4] first draft for multiple user models feature --- pages/en/lb3/Accessing-related-models.md | 5 +- ...ntication-authorization-and-permissions.md | 203 ++++++++++++++++-- pages/en/lb3/Controlling-data-access.md | 28 ++- pages/en/lb3/Model-definition-JSON-file.md | 12 +- pages/en/lb3/Using-built-in-models.md | 2 +- 5 files changed, 213 insertions(+), 37 deletions(-) diff --git a/pages/en/lb3/Accessing-related-models.md b/pages/en/lb3/Accessing-related-models.md index 11a47bb9b..3e628ae77 100644 --- a/pages/en/lb3/Accessing-related-models.md +++ b/pages/en/lb3/Accessing-related-models.md @@ -11,8 +11,9 @@ summary: For related models, LoopBack automatically related model methods ---
-{% include important.html content="When accessing a related model, the active ACL is still the one for the model you are calling. -So even if your model has DENY ALL permissions set, if the model relating to it has no ACL, then all the relation endpoints will be open. This can be a security risk because, for example, `GET /OpenModel/{id}/ACLSecuredModel` will allow full access to `ACLSecuredModel` through the `OpenModel` relations. +{% include important.html content="When accessing a related model, the effective ACL (the one considered to resolve access permission) is the one for the model you are primarily calling. +So if a given model has no ACL, then all the endpoints accessible through the model relations will be open, even if a related model has DENY ALL permissions set.
+This can be a security risk since, for example, `GET /OpenModel/{id}/ACLSecuredModel` will allow full access to `ACLSecuredModel` through the `OpenModel` relations. " %} ## Restricting access to related models diff --git a/pages/en/lb3/Authentication-authorization-and-permissions.md b/pages/en/lb3/Authentication-authorization-and-permissions.md index fdbb6ff59..6991a4dc2 100644 --- a/pages/en/lb3/Authentication-authorization-and-permissions.md +++ b/pages/en/lb3/Authentication-authorization-and-permissions.md @@ -20,16 +20,6 @@ LoopBack apps access data through models (see [Defining models](Defining-models so controlling access to data means putting restrictions on models; that is, specifying who or what can read/write the data or execute methods on the models.  -When you create your app with the LoopBack [application generator](Application-generator.html), access control is automatically enabled, _except_ if you choose the "empty-server" application type. -To enable access control for an "empty-server" application, you must add a boot -script that calls `enableAuth()`. For example, in `server/boot/authentication.js`: - -```js -module.exports = function enableAuthentication(server) { - server.enableAuth(); -}; -``` - ## Access control concepts LoopBack's access control system is built around a few core concepts, as summarized in the following table.  @@ -45,15 +35,196 @@ LoopBack's access control system is built around a few core concepts, as summari The general process to implement access control for an application is: -1. **Specify user roles**. +1. **Implement authentication**: + In the application, add code to create (register) new users, login users (get and use authentication tokens), and logout users. +2. **Specify user roles**. Define the user roles that your application requires. - For example, you might create roles for anonymous users, authorized users, and administrators.  -2. **Define access for each role and model method**. + For example, you might create roles for anonymous users, authenticated users, group members, and administrators.  +3. **Define access for each role and model method**. For example, you might enable anonymous users to read a list of banks, but not allow them to do anything else. LoopBack models have a set of built-in methods, and each method maps to either the READ or WRITE access type. - In essence, this step amounts to specifying whether access is allowed for each role and each Model - access type, as illustrated in the example below. -3. **Implement authentication**: - in the application, add code to create (register) new users, login users (get and use authentication tokens), and logout users. + In essence, this step amounts to specifying whether access is allowed for each role and each Model - access type, as illustrated in the example below.
+ _NOTE: you can also map directly access rights to specific users or applications._ +4. **Set-up access control for users**: + This can be achieved either by statically mapping users with any role you may have earlier created using the Role model, **or** by adding code to register dynamic role resolvers that will at runtime resolve whether a user, given a preconfigured set of conditions, has a given role. + +## Initial setup + +### Enabling Access control + +When you create your app with the LoopBack [application generator](Application-generator.html), access control is automatically enabled, _except_ if you choose the "empty-server" application type. +To enable access control for an "empty-server" application, you must add a boot +script that calls `enableAuth()`. For example, in `server/boot/authentication.js`: + +```js +module.exports = function enableAuthentication(server) { + server.enableAuth(); +}; +``` + +### Preparing Access control models + +Then you need to make sure the `User`, and optionaly the `AccessToken` models are configured appropriately according to your set-up. + +{% include note.html content=" +Usually you don't need any extra configuration regarding built-in models `Role`, `RoleMapping`, and `ACL` which you can use without any customization. Just make sure they are declared in the `model-config.json` configuration file. +" %} + +
+Normally you should have already implemented at least one custom user model, extending the built-in `User` model, as described in [this section](Using-built-in-models.html#user-model). + +{% include tip.html content=" +Whether you can use the built-in `AccessToken` model or **require** to create a custom accessToken model extending the built-in model depends on whether you plan to use a single or several user models extending the built-in `User` model. Both cases are covered in the next two sections. +" %} + +#### Access Control with a single user model + +In the case your application leverages only one type of user extending the built-in `User` model (which should the case in a majority of configurations), you barely have no further configuration to do. + +1. Rely on the built-in AccessToken model. +2. Make sure your custom user model implements a hasMany relation with the AccessToken model as follows: + +{% include code-caption.html content="/common/models/custom-user.json" %} +```json +... +"relations": { + "accessTokens": { + "type": "hasMany", + "model": "AccessToken", + "foreignKey": "userId", + "options": { + "disableInclude": true + } + } +}, +... +``` + +#### Access Control with multiple user models + +##### Purpose + +While this is not a day-to-day scenario, it may be required in more advanced situations to deal with significantly different types of users in a Loopback application. + +In some of these situations where the different types of users mostly differ by a limited amount of properties a standard fallback consists in overloading a single custom user model with all properties required by all types of users and differentiate the access control behavior with static roles mapped to the different types of users. + +On the other hand one could be facing situations where the types of users involved not only differ from one another by their properties, but also by their nature in the application and thus by their relations with other models. Examples of such applications include for example applications where the concept of Organization is involved, inducing intertwined layers of relationships and thus of access control. + +
+Consider the following example: +- **Application** has `Users`: _app-admins, app-managers, app-auditors, etc._ +- **Application** has Organizations +- **Organizations** have `Users`: _org-admins, org-managers, org-marketing, org-sales_ +- **Organizations** have Customers (which are also `Users`) + +Such an application sums up 3 different types of users in the application : `App-Managers`, `Org-Managers`, `Org-Customers`. Each of these 3 types of users have very different relations and rights with the models composing the application. +Such circumstances require the application to actually manage these different types of users in separated models. + +##### Setup + +In order to allow the Loopback access control system to work with several models extending the built-in `User` model. The relations between the `users` models and the `AccessToken` should be modified to allow a single `AccessToken` model to host access tokens for multiple types of users while at the same time allowing each `user` models instances to be linked to their related access tokens in a non ambiguous way. + +This is achieved by changing the **hasMany** relation from `User` to `AccessToken` and the **belongsTo** relation from `AccessToken` to `User` by their [polymorphic](Polymorphic-relations) equivalents, in which the `principalType` property is used as a **_discriminator_** to resolve which of the potential `user` model instance an 'accessToken' instance belongs to. + +
+{% include note.html content=" +Adapt the following configuration snippets in your custom users and accessToken model definitions. +"%} + +{% include code-caption.html content="/common/models/any-custom-user.json" %} +```json +... +"relations": { + "accessTokens": { + "type": "hasMany", + "model": "customAccessToken", + "polymorphic": { + "foreignKey": "userId", + "discriminator": "principalType" + }, + "options": { + "disableInclude": true + } + } +}, +... +``` + +{% include code-caption.html content="/common/models/custom-access-token.json" %} +```json +... +"relations": { + "user": { + "type": "belongsTo", + "idName": "id", + "polymorphic": { + "idType": "string", + "foreignKey": "userId", + "discriminator": "principalType" + } + } +}, +... +``` + +{% include important.html content=" +In particular, pay attention to: + +" %} + +
+**Last but not least**, do not forget to tell Loopback to use the newly defined custom `accessToken` model by including these lines in the `server.js` file or in a boot script, once again paying attention to the _name_ of the custom `accessToken` model. + +```javascript +var loopback = require('loopback'); +... +app.use(loopback.token({ + model: app.models.customAccessToken +})); +``` +
+{% include tip.html content=" +**Well done, you're all set!** +From this point you should be able to use Loopback access control over multiple user models extending the built-in `User` model, with barely no modification compared to the way you're used to handle it with a single user model. +" %} + +##### Methods and parameters impacted when using multiple user models + +{% include important.html content=" +Pay attention to the following methods and parameters impacted by the switch to multiple user models support. +" %} + +* Anytime a method is expecting the **principalType** for a principal of the `User` type (as-is or nested in an [AccessContext](https://apidocs.strongloop.com/loopback/#accesscontext) object), provide the name of the targeted `user` model name (e.g. `'oneCustomUserModelName'`) instead of the usual `Principal.USER` (or `'USER'`).
+Such methods include: `Role.getRoles()` and `Role.isInRole()`. For example: + +```javascript +Role.getRoles({ + principalType: 'oneCustomUserModelName', + principalId: 123, +}); +``` + +* `Role` instance method `Role.prototype.users()`: the method which return all the users mapped with a given role instance should now be called with the following syntax: + +```javascript +roleInstance.users({where: { + principalType: 'oneCustomUserModelName' +}); +``` + +* `RoleMapping` static methods: these methods either accessed directly or through the relation `principals` of the `Role` model should also use the new `principalType` syntax, for example: + +```javascript +roleInstance.principals.create({ + principalType: 'oneCustomUserModelName', + principalId: 123 +}); +``` ## Exposing and hiding models, methods, and endpoints diff --git a/pages/en/lb3/Controlling-data-access.md b/pages/en/lb3/Controlling-data-access.md index af7894557..cda128117 100644 --- a/pages/en/lb3/Controlling-data-access.md +++ b/pages/en/lb3/Controlling-data-access.md @@ -161,21 +161,19 @@ Each incoming request is mapped to an object with three attributes: ACL rules are described as an array of objects, each of which consists of attributes listed at  [Model definition JSON file - ACLs](Model-definition-JSON-file.html#acls).  -1. model -2. property -3. accessType -4. principalType - 1. USER - 2. APP - 3. ROLE - 1. custom roles - 2. $owner - 3. $authenticated - 4. $unauthenticated - 5. $everyone -5. permission - 1. DENY - 2. ALLOW +* model +* property +* accessType +* principalType + * USER: a user ID + * APP: an application ID + * ROLE: a role name + * _built-in dynamic roles_, one of [`$everyone`, `$unauthenticated`, `$authenticated`, `$owner`] + * [_custom static roles_](Defining-and-using-roles.html#static-roles), directly mapped to principals + * [_custom dynamic roles_](Defining-and-using-roles.html#dynamic-roles), registered with custom role resolvers +* permission + * DENY + * ALLOW ### ACL rule precedence diff --git a/pages/en/lb3/Model-definition-JSON-file.md b/pages/en/lb3/Model-definition-JSON-file.md index 3bf1f1d0e..2f3271585 100644 --- a/pages/en/lb3/Model-definition-JSON-file.md +++ b/pages/en/lb3/Model-definition-JSON-file.md @@ -783,17 +783,23 @@ The value of the `acls` key is an array of objects that describes the access The value must be one of: @@ -804,7 +810,7 @@ The value of the `acls` key is an array of objects that describes the access Type of the principal. Required. One of: diff --git a/pages/en/lb3/Using-built-in-models.md b/pages/en/lb3/Using-built-in-models.md index 2578ff5dc..1fcecb7a7 100644 --- a/pages/en/lb3/Using-built-in-models.md +++ b/pages/en/lb3/Using-built-in-models.md @@ -40,7 +40,7 @@ The default model definition file is [common/models/user.json](https://github.c {% include important.html content=" You must create your own custom model (named something other than \"User,\" for example \"Customer\" or \"Client\") that [extends the built-in User model](Extending-built-in-models.html) rather than use the built-in User model directly. The built-in User model provides a great deal of commonly-used functionality that you can use via your custom model. -LoopBack does not support multiple models based on the User model in a single application. That is, you cannot have more than one model derived from the built-in User model in a single app. +Since version 3, Loopback supports multiple models based on the User model in a single application. See [this section](...) on how to configure the application accordingly. " %} For more information, see [Managing users](Managing-users.html). From f2b63fd2135cf24bbf509f7397f6d6fa23c6769a Mon Sep 17 00:00:00 2001 From: ebarault Date: Wed, 1 Mar 2017 19:03:14 +0100 Subject: [PATCH 2/4] revision following @bajtos review --- ...ntication-authorization-and-permissions.md | 50 ++++++++++++++----- pages/en/lb3/Using-built-in-models.md | 2 +- 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/pages/en/lb3/Authentication-authorization-and-permissions.md b/pages/en/lb3/Authentication-authorization-and-permissions.md index 6991a4dc2..9ca6c5da0 100644 --- a/pages/en/lb3/Authentication-authorization-and-permissions.md +++ b/pages/en/lb3/Authentication-authorization-and-permissions.md @@ -54,9 +54,10 @@ The general process to implement access control for an application is: When you create your app with the LoopBack [application generator](Application-generator.html), access control is automatically enabled, _except_ if you choose the "empty-server" application type. To enable access control for an "empty-server" application, you must add a boot -script that calls `enableAuth()`. For example, in `server/boot/authentication.js`: +script that calls [`enableAuth()`](https://apidocs.strongloop.com/loopback/#app-enableauth) as shown in the following example: -```js +{% include code-caption.html content="server/boot/authentication.js" %} +```javascript module.exports = function enableAuthentication(server) { server.enableAuth(); }; @@ -66,13 +67,18 @@ module.exports = function enableAuthentication(server) { Then you need to make sure the `User`, and optionaly the `AccessToken` models are configured appropriately according to your set-up. +Normally you should have already implemented at least one custom user model, extending the built-in `User` model, as described in [this section](Using-built-in-models.html#user-model). + +Usually you don't need any extra configuration regarding built-in models `Role`, `RoleMapping`, and `ACL` which you can be use without any customization. Just make sure they are declared in the `model-config.json` configuration file, or in case you don't require either to customize the `AccessToken` model that you pass a datasource to the `enableAuth()` method as follows: + +```javascript +server.enableAuth({ datasource: 'db' }); +``` + {% include note.html content=" -Usually you don't need any extra configuration regarding built-in models `Role`, `RoleMapping`, and `ACL` which you can use without any customization. Just make sure they are declared in the `model-config.json` configuration file. +Passing a `datasource` to the `enableAuth()` method as shown here will let Loopback take care of attaching any built-in model required by the Access Control feature, which is perfectly suited for most applications. " %} -
-Normally you should have already implemented at least one custom user model, extending the built-in `User` model, as described in [this section](Using-built-in-models.html#user-model). - {% include tip.html content=" Whether you can use the built-in `AccessToken` model or **require** to create a custom accessToken model extending the built-in model depends on whether you plan to use a single or several user models extending the built-in `User` model. Both cases are covered in the next two sections. " %} @@ -81,10 +87,10 @@ Whether you can use the built-in `AccessToken` model or **require** to create a In the case your application leverages only one type of user extending the built-in `User` model (which should the case in a majority of configurations), you barely have no further configuration to do. -1. Rely on the built-in AccessToken model. +1. Rely on the built-in `AccessToken` model. 2. Make sure your custom user model implements a hasMany relation with the AccessToken model as follows: -{% include code-caption.html content="/common/models/custom-user.json" %} +{% include code-caption.html content="common/models/custom-user.json" %} ```json ... "relations": { @@ -122,16 +128,20 @@ Such circumstances require the application to actually manage these different ty ##### Setup +{% include important.html content=" +When using multiple user models, you should not let Loopback auto-attach built-in models required by the Access Control feature. Instead, call the `enableAuth()` method with no argument and manually define all models required in the `server/model-config.json` configuration file. +" %} + In order to allow the Loopback access control system to work with several models extending the built-in `User` model. The relations between the `users` models and the `AccessToken` should be modified to allow a single `AccessToken` model to host access tokens for multiple types of users while at the same time allowing each `user` models instances to be linked to their related access tokens in a non ambiguous way. -This is achieved by changing the **hasMany** relation from `User` to `AccessToken` and the **belongsTo** relation from `AccessToken` to `User` by their [polymorphic](Polymorphic-relations) equivalents, in which the `principalType` property is used as a **_discriminator_** to resolve which of the potential `user` model instance an 'accessToken' instance belongs to. +This is achieved by changing the **hasMany** relation from `User` to `AccessToken` and the **belongsTo** relation from `AccessToken` to `User` by their [polymorphic](Polymorphic-relations.html) equivalents, in which the `principalType` property is used as a **_discriminator_** to resolve which of the potential `user` model instance an 'accessToken' instance belongs to.
{% include note.html content=" Adapt the following configuration snippets in your custom users and accessToken model definitions. "%} -{% include code-caption.html content="/common/models/any-custom-user.json" %} +{% include code-caption.html content="common/models/any-custom-user.json" %} ```json ... "relations": { @@ -150,7 +160,7 @@ Adapt the following configuration snippets in your custom users and ... ``` -{% include code-caption.html content="/common/models/custom-access-token.json" %} +{% include code-caption.html content="common/models/custom-access-token.json" %} ```json ... "relations": { @@ -178,8 +188,24 @@ In particular, pay attention to: " %}
-**Last but not least**, do not forget to tell Loopback to use the newly defined custom `accessToken` model by including these lines in the `server.js` file or in a boot script, once again paying attention to the _name_ of the custom `accessToken` model. +**Last but not least**, do not forget to tell Loopback to use the newly defined custom `accessToken` model by configuring accordingly + +{% include code-caption.html content="server/middleware.json" %} +```json +{ + "auth": { + "loopback#token": { + "params": { + "model": "customAccessToken" + } + } + } +} +``` + +**Note**: this can also be achieved by including these lines in the `server.js` file or in a boot script, once again paying attention to the _name_ of the custom `accessToken` model. +{% include code-caption.html content="server/server.js" %} ```javascript var loopback = require('loopback'); ... diff --git a/pages/en/lb3/Using-built-in-models.md b/pages/en/lb3/Using-built-in-models.md index 1fcecb7a7..6e2ad9dfa 100644 --- a/pages/en/lb3/Using-built-in-models.md +++ b/pages/en/lb3/Using-built-in-models.md @@ -40,7 +40,7 @@ The default model definition file is [common/models/user.json](https://github.c {% include important.html content=" You must create your own custom model (named something other than \"User,\" for example \"Customer\" or \"Client\") that [extends the built-in User model](Extending-built-in-models.html) rather than use the built-in User model directly. The built-in User model provides a great deal of commonly-used functionality that you can use via your custom model. -Since version 3, Loopback supports multiple models based on the User model in a single application. See [this section](...) on how to configure the application accordingly. +Since version 3, Loopback supports multiple models based on the User model in a single application. See [this section](Authentication-authorization-and-permissions.html#access-control-with-multiple-user-models) on how to configure the application accordingly. " %} For more information, see [Managing users](Managing-users.html). From 69edc986723d92ed610bf8685ef755ca8d86d0cc Mon Sep 17 00:00:00 2001 From: crandmck Date: Wed, 1 Mar 2017 22:40:51 -0800 Subject: [PATCH 3/4] Editing, rewording, formatting, etc --- pages/en/lb3/Accessing-related-models.md | 3 +- ...ntication-authorization-and-permissions.md | 103 +++++++++--------- pages/en/lb3/Model-definition-JSON-file.md | 2 +- 3 files changed, 55 insertions(+), 53 deletions(-) diff --git a/pages/en/lb3/Accessing-related-models.md b/pages/en/lb3/Accessing-related-models.md index 3e628ae77..05d3e94d0 100644 --- a/pages/en/lb3/Accessing-related-models.md +++ b/pages/en/lb3/Accessing-related-models.md @@ -12,7 +12,8 @@ summary: For related models, LoopBack automatically related model methods
{% include important.html content="When accessing a related model, the effective ACL (the one considered to resolve access permission) is the one for the model you are primarily calling. -So if a given model has no ACL, then all the endpoints accessible through the model relations will be open, even if a related model has DENY ALL permissions set.
+So if a given model has no ACL, then all the endpoints accessible through the model relations will be open, even if a related model has DENY ALL permissions set. + This can be a security risk since, for example, `GET /OpenModel/{id}/ACLSecuredModel` will allow full access to `ACLSecuredModel` through the `OpenModel` relations. " %} diff --git a/pages/en/lb3/Authentication-authorization-and-permissions.md b/pages/en/lb3/Authentication-authorization-and-permissions.md index 9ca6c5da0..742fb85f5 100644 --- a/pages/en/lb3/Authentication-authorization-and-permissions.md +++ b/pages/en/lb3/Authentication-authorization-and-permissions.md @@ -2,6 +2,7 @@ title: "Authentication, authorization, and permissions" lang: en layout: navgroup +toc_level: 2 navgroup: user-mgmt keywords: LoopBack tags: authentication @@ -24,7 +25,7 @@ specifying who or what can read/write the data or execute methods on the models. LoopBack's access control system is built around a few core concepts, as summarized in the following table.  -| Term | Description | Responsibility | Example | +| Term       | Description | Responsibility | Example | |---|---|---|---| | Principal | An entity that can be identified or authenticated. | Represents identities of a request to protected resources. | A user
An application
A role (please note a role is also a principal) | | Role | A group of principals with the same permissions. | Organizes principals into groups so they can be used. | **Dynamic role**: 
`$everyone` (for all users)
`$unauthenticated` (unauthenticated users)
`$owner` (the principal is owner of the model instance), which can be:
  ◦ A simple property called `userId`
  ◦ A simple property called `owner`
  ◦ A relation to a model that extends User.

**Static role**: admin (a defined role for administrators) | @@ -36,25 +37,27 @@ LoopBack's access control system is built around a few core concepts, as summari The general process to implement access control for an application is: 1. **Implement authentication**: - In the application, add code to create (register) new users, login users (get and use authentication tokens), and logout users. + In the application, add code to create (register) new users, log in users (get and use authentication tokens), and log out users. 2. **Specify user roles**. Define the user roles that your application requires. For example, you might create roles for anonymous users, authenticated users, group members, and administrators.  3. **Define access for each role and model method**. For example, you might enable anonymous users to read a list of banks, but not allow them to do anything else. LoopBack models have a set of built-in methods, and each method maps to either the READ or WRITE access type. - In essence, this step amounts to specifying whether access is allowed for each role and each Model - access type, as illustrated in the example below.
- _NOTE: you can also map directly access rights to specific users or applications._ -4. **Set-up access control for users**: - This can be achieved either by statically mapping users with any role you may have earlier created using the Role model, **or** by adding code to register dynamic role resolvers that will at runtime resolve whether a user, given a preconfigured set of conditions, has a given role. + In essence, this step amounts to specifying whether access is allowed for each role and each Model/access type, as illustrated in the example below.

+ NOTE: you can also map access rights directly to specific users or applications. +4. **Set-up access control for users**. Do one of: + - Statically map users with any role previously created using the Role model. For more information, see [Static roles](Defining-and-using-roles.html#static-roles). + - Add code to register dynamic role resolvers that at runtime resolve whether a user, given a preconfigured set of conditions, has a given role. For more information, see [Dynamic roles](Defining-and-using-roles.html#dynamic-roles). ## Initial setup -### Enabling Access control +### Enabling access control -When you create your app with the LoopBack [application generator](Application-generator.html), access control is automatically enabled, _except_ if you choose the "empty-server" application type. -To enable access control for an "empty-server" application, you must add a boot -script that calls [`enableAuth()`](https://apidocs.strongloop.com/loopback/#app-enableauth) as shown in the following example: +Applications created with the LoopBack [application generator](Application-generator.html) +have access control enabled by default, _except_ for the "empty-server" application type. +To enable access control for an "empty-server" application, add a boot +script that calls [`enableAuth()`](https://apidocs.strongloop.com/loopback/#app-enableauth); for example: {% include code-caption.html content="server/boot/authentication.js" %} ```javascript @@ -63,29 +66,29 @@ module.exports = function enableAuthentication(server) { }; ``` -### Preparing Access control models +### Preparing access control models -Then you need to make sure the `User`, and optionaly the `AccessToken` models are configured appropriately according to your set-up. +Make sure the `User` model, (and possibly the `AccessToken` model) is configured appropriately according to your set-up. -Normally you should have already implemented at least one custom user model, extending the built-in `User` model, as described in [this section](Using-built-in-models.html#user-model). +Normally you should have already implemented at least one custom user model, extending the built-in `User` model, as described in [Using built-in models](Using-built-in-models.html#user-model). -Usually you don't need any extra configuration regarding built-in models `Role`, `RoleMapping`, and `ACL` which you can be use without any customization. Just make sure they are declared in the `model-config.json` configuration file, or in case you don't require either to customize the `AccessToken` model that you pass a datasource to the `enableAuth()` method as follows: +Usually you don't need to extend or customize the built-in models `Role`, `RoleMapping`, and `ACL`. Just make sure they are declared in the `model-config.json` configuration file, or in case you don't require either to customize the `AccessToken` model that you pass a datasource to the `enableAuth()` method as follows: ```javascript server.enableAuth({ datasource: 'db' }); ``` {% include note.html content=" -Passing a `datasource` to the `enableAuth()` method as shown here will let Loopback take care of attaching any built-in model required by the Access Control feature, which is perfectly suited for most applications. +Passing a `datasource` to the `enableAuth()` method as shown here will let Loopback take care of attaching any built-in model required by the access control feature, which is perfectly suited for most applications. " %} {% include tip.html content=" -Whether you can use the built-in `AccessToken` model or **require** to create a custom accessToken model extending the built-in model depends on whether you plan to use a single or several user models extending the built-in `User` model. Both cases are covered in the next two sections. +Whether you can use the built-in `AccessToken` model or create a custom accessToken model extending the built-in model depends on whether you plan to use one or several user models extending the built-in `User` model. Both cases are covered in the next two sections. " %} -#### Access Control with a single user model +#### Access control with a single user model -In the case your application leverages only one type of user extending the built-in `User` model (which should the case in a majority of configurations), you barely have no further configuration to do. +If your application leverages only one type of user extending the built-in `User` model (which should the case in a majority of configurations), you have little further configuration to do. 1. Rely on the built-in `AccessToken` model. 2. Make sure your custom user model implements a hasMany relation with the AccessToken model as follows: @@ -106,39 +109,41 @@ In the case your application leverages only one type of user extending the built ... ``` -#### Access Control with multiple user models +#### Access control with multiple user models -##### Purpose +Having multiple user models may be required in certain situations to deal with significantly different types of users in an application. -While this is not a day-to-day scenario, it may be required in more advanced situations to deal with significantly different types of users in a Loopback application. +If the types of users differ by just a few properties, then it's easiest to overload a single custom user model with all properties required, and differentiate access control behavior with static roles mapped to the different user types. -In some of these situations where the different types of users mostly differ by a limited amount of properties a standard fallback consists in overloading a single custom user model with all properties required by all types of users and differentiate the access control behavior with static roles mapped to the different types of users. +A more complex situation is when the different types of users differ not just in their properties, but also by their access rights and relations with other models. For example, applications where the concept of an _organization_ is involved, creating intertwined layers of relationships and thus of access control. +Such circumstances require the application to manage the different user types in separate models. -On the other hand one could be facing situations where the types of users involved not only differ from one another by their properties, but also by their nature in the application and thus by their relations with other models. Examples of such applications include for example applications where the concept of Organization is involved, inducing intertwined layers of relationships and thus of access control. - -
Consider the following example: + - **Application** has `Users`: _app-admins, app-managers, app-auditors, etc._ - **Application** has Organizations - **Organizations** have `Users`: _org-admins, org-managers, org-marketing, org-sales_ - **Organizations** have Customers (which are also `Users`) -Such an application sums up 3 different types of users in the application : `App-Managers`, `Org-Managers`, `Org-Customers`. Each of these 3 types of users have very different relations and rights with the models composing the application. -Such circumstances require the application to actually manage these different types of users in separated models. +Such an application has three different types of users: + +- `App-Managers` +- `Org-Managers` +- `Org-Customers` + +Each type of user has different relations with and access rights to the models composing the application. ##### Setup {% include important.html content=" -When using multiple user models, you should not let Loopback auto-attach built-in models required by the Access Control feature. Instead, call the `enableAuth()` method with no argument and manually define all models required in the `server/model-config.json` configuration file. +When using multiple user models, you should not let LoopBack auto-attach built-in models required by the access control feature. Instead, call the `enableAuth()` method with no argument and manually define all models required in the `server/model-config.json` configuration file. " %} -In order to allow the Loopback access control system to work with several models extending the built-in `User` model. The relations between the `users` models and the `AccessToken` should be modified to allow a single `AccessToken` model to host access tokens for multiple types of users while at the same time allowing each `user` models instances to be linked to their related access tokens in a non ambiguous way. +To use several models extending the built-in `User` model, you must modify the relations between the `users` models and the `AccessToken` models to allow a single `AccessToken` model to host access tokens for multiple types of users while at the same time allowing each `user` model instance to be linked to unique related access tokens. -This is achieved by changing the **hasMany** relation from `User` to `AccessToken` and the **belongsTo** relation from `AccessToken` to `User` by their [polymorphic](Polymorphic-relations.html) equivalents, in which the `principalType` property is used as a **_discriminator_** to resolve which of the potential `user` model instance an 'accessToken' instance belongs to. +This is achieved by changing the **hasMany** relation from `User` to `AccessToken` and the **belongsTo** relation from `AccessToken` to `User` by their [polymorphic](Polymorphic-relations.html) equivalents, in which the `principalType` property is used as a _discriminator_ to resolve which of the potential `user` model instance an 'accessToken' instance belongs to. -
-{% include note.html content=" -Adapt the following configuration snippets in your custom users and accessToken model definitions. +{% include note.html content="Adapt the following configuration snippets in your custom `users` and `accessToken` model definitions. "%} {% include code-caption.html content="common/models/any-custom-user.json" %} @@ -179,16 +184,14 @@ Adapt the following configuration snippets in your custom users and {% include important.html content=" In particular, pay attention to: -
    -
  • the model name used to refer to the access token model in the different user models (here named \"customAccessToken\")
  • -
  • the idName used for the foreignKey in the access token model referring to the user instance (here named \"id\")
  • -
  • the idType used for this foreignKey, according to the type of connector used for the related user models (here using \"string\" for a MongoDB connector for example)
  • -
  • use \"principalType\" for the discriminator name, this is mandatory and cannot be changed
  • -
+ +* The `model` name used to refer to the access token model in the different user models (here named \"customAccessToken\") +* The `idName` used for the foreignKey in the access token model referring to the user instance (here named \"id\") +* The `idType` used for this foreignKey, according to the type of connector used for the related user models (here using \"string\" for a MongoDB connector for example) +* Use \"principalType\" for the `discriminator` name. This is mandatory and cannot be changed " %} -
-**Last but not least**, do not forget to tell Loopback to use the newly defined custom `accessToken` model by configuring accordingly +Don't forget to specify the custom `accessToken` model as follows: {% include code-caption.html content="server/middleware.json" %} ```json @@ -203,7 +206,7 @@ In particular, pay attention to: } ``` -**Note**: this can also be achieved by including these lines in the `server.js` file or in a boot script, once again paying attention to the _name_ of the custom `accessToken` model. +**Note**: Alternatively, you can put these lines in the `server.js` file or in a boot script, once again paying attention to the _name_ of the custom `accessToken` model. {% include code-caption.html content="server/server.js" %} ```javascript @@ -213,10 +216,8 @@ app.use(loopback.token({ model: app.models.customAccessToken })); ``` -
-{% include tip.html content=" -**Well done, you're all set!** -From this point you should be able to use Loopback access control over multiple user models extending the built-in `User` model, with barely no modification compared to the way you're used to handle it with a single user model. + +{% include tip.html content="From this point you should be able to use LoopBack access control over multiple user models extending the built-in `User` model, with barely no modification compared to the way you're used to handle it with a single user model. " %} ##### Methods and parameters impacted when using multiple user models @@ -225,7 +226,7 @@ From this point you should be able to use Loopback access control over multiple Pay attention to the following methods and parameters impacted by the switch to multiple user models support. " %} -* Anytime a method is expecting the **principalType** for a principal of the `User` type (as-is or nested in an [AccessContext](https://apidocs.strongloop.com/loopback/#accesscontext) object), provide the name of the targeted `user` model name (e.g. `'oneCustomUserModelName'`) instead of the usual `Principal.USER` (or `'USER'`).
+Anytime a method is expecting the **principalType** for a principal of the `User` type (as-is or nested in an [AccessContext](https://apidocs.strongloop.com/loopback/#accesscontext) object), provide the name of the targeted `user` model name (e.g. `'oneCustomUserModelName'`) instead of the usual `Principal.USER` (or `'USER'`).
Such methods include: `Role.getRoles()` and `Role.isInRole()`. For example: ```javascript @@ -235,7 +236,7 @@ Role.getRoles({ }); ``` -* `Role` instance method `Role.prototype.users()`: the method which return all the users mapped with a given role instance should now be called with the following syntax: +`Role` instance method `Role.prototype.users()`: the method which return all the users mapped with a given role instance should now be called with the following syntax: ```javascript roleInstance.users({where: { @@ -243,7 +244,7 @@ roleInstance.users({where: { }); ``` -* `RoleMapping` static methods: these methods either accessed directly or through the relation `principals` of the `Role` model should also use the new `principalType` syntax, for example: +`RoleMapping` static methods: these methods either accessed directly or through the relation `principals` of the `Role` model should also use the new `principalType` syntax, for example: ```javascript roleInstance.principals.create({ @@ -321,9 +322,9 @@ MyUser.disableRemoteMethod('__get__accessTokens', false); MyUser.disableRemoteMethod('__updateById__accessTokens', false); ``` -### Read-Only endpoints example +### Read-only endpoints example -You may want to only expose read-only operations on your model hiding all POST, PUT, DELETE verbs +You may want to only expose read-only operations on your model; in other words hiding all operations that use HTTP POST, PUT, DELETE method. **common/models/model.js** diff --git a/pages/en/lb3/Model-definition-JSON-file.md b/pages/en/lb3/Model-definition-JSON-file.md index 2f3271585..6e65ae226 100644 --- a/pages/en/lb3/Model-definition-JSON-file.md +++ b/pages/en/lb3/Model-definition-JSON-file.md @@ -740,7 +740,7 @@ The value of the `acls` key is an array of objects that describes the access - + From 32d6cb1bb08d7dee86436d4babf7c987adadc4b1 Mon Sep 17 00:00:00 2001 From: crandmck Date: Thu, 2 Mar 2017 13:08:55 -0800 Subject: [PATCH 4/4] Final edits --- pages/en/lb3/Authentication-authorization-and-permissions.md | 2 +- pages/en/lb3/Using-built-in-models.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pages/en/lb3/Authentication-authorization-and-permissions.md b/pages/en/lb3/Authentication-authorization-and-permissions.md index 742fb85f5..3b3f5df9f 100644 --- a/pages/en/lb3/Authentication-authorization-and-permissions.md +++ b/pages/en/lb3/Authentication-authorization-and-permissions.md @@ -79,7 +79,7 @@ server.enableAuth({ datasource: 'db' }); ``` {% include note.html content=" -Passing a `datasource` to the `enableAuth()` method as shown here will let Loopback take care of attaching any built-in model required by the access control feature, which is perfectly suited for most applications. +Passing a `datasource` to the `enableAuth()` method as shown here will let LoopBack take care of attaching any built-in models required by the access control feature, which is suitable for most applications. " %} {% include tip.html content=" diff --git a/pages/en/lb3/Using-built-in-models.md b/pages/en/lb3/Using-built-in-models.md index 6e2ad9dfa..01daf5b82 100644 --- a/pages/en/lb3/Using-built-in-models.md +++ b/pages/en/lb3/Using-built-in-models.md @@ -11,7 +11,7 @@ summary: ## Overview -Loopback provides useful built-in models for common use cases: +LoopBack provides useful built-in models for common use cases: * **[Application model](#application-model)** - contains metadata for a client application that has its own identity and associated configuration with the LoopBack server. * **[User model](#user-model)** - register and authenticate users of your app locally or against third-party services. @@ -40,7 +40,7 @@ The default model definition file is [common/models/user.json](https://github.c {% include important.html content=" You must create your own custom model (named something other than \"User,\" for example \"Customer\" or \"Client\") that [extends the built-in User model](Extending-built-in-models.html) rather than use the built-in User model directly. The built-in User model provides a great deal of commonly-used functionality that you can use via your custom model. -Since version 3, Loopback supports multiple models based on the User model in a single application. See [this section](Authentication-authorization-and-permissions.html#access-control-with-multiple-user-models) on how to configure the application accordingly. +Starting with version 3.3.0, LoopBack supports applications with multiple models based on the User model. For more information, see [Access control with multiple user models](Authentication-authorization-and-permissions.html#access-control-with-multiple-user-models). " %} For more information, see [Managing users](Managing-users.html).
KeyKey Type Description