When you create your app with the LoopBack application generator, 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:
module.exports = function enableAuthentication(server) {
server.enableAuth();
};
The first step in specifying user roles is to determine what roles your application needs. Most applications will have un-authenticated or anonymous users (those who have not logged in) and authenticated users (those who have logged in). Additionally, many applications will have an administrative role that provides broad access rights. And applications can have any number of additional user roles as appropriate.
For example, the
startkicker
app consists of four types of users: guest, owner, team member and administrator.
Each user type has access to various parts of the app based on their role and the access control lists (ACLs) we define.
LoopBack provides a built-in User model with a corresponding REST API that inherits all the "CRUD" (create, read, update, and delete) methods of the PersistedModel object. Each CRUD method of the LoopBack User model maps to either the READ or WRITE access type, as follows:
READ:
WRITE:
For other methods, the default access type is EXECUTE; for example, a custom method maps to the EXECUTE access type.
Use the ACL generator (apic loopback:acl) to set up access control for an application.
Before you do that, though, you must have a clear idea of how you’re going to configure access control for your application.
For example, here is how loopback-example-access-control sets up users and their rights:
Once you’ve created this kind of specification, you can easily construct apic loopback:acl commands to set up access control, as illustrated below.
The easiest way to define access control for an app is with the ACL generator. This enables you to create a static definition before runtime. The generator prompts you for all the necessary information:
$ slc loopback:acl
$ apic loopback:acl
For example, here are the answers to prompts to define ACL entries for the loopback-example-access-control example.
Deny access to all project REST endpoints
Allow unrestricted access to GET /api/projects/listProjects
Only allow admin unrestricted access to GET /api/projects
Only allow team members access to GET /api/projects/:id
Allow authenticated users to access POST /api/projects/donate
Allow owners access to POST /api/projects/withdraw
For more information, see the online help (apic loopback:acl -h) .
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.
A single model may have several ACLs applied to it: The ACL of the base model (or models) and that of the model itself, defined in the model definition JSON file. LoopBack determines the ultimate ACL by adding all the applicable ACLs with precedence rules for permission and access type to resolve any conflicts.
Permission precedence is applied in this order:
So, for example, a DENY rule for a certain operation and user group will take precedence over an ALLOW rule for the same operation and group.
Access type precedence (in order of specificity) is applied in this order:
In general, a more specific rule will take precedence over a more general rule. For example, a rule that denies access to an operation to authenticated users will take precedence over a rule that denies access to all users.
LoopBack sorts multiple rules by the specifics of matching the request against each rule. It calculates the specifics by checking the access request against each ACL rule by the hierarchical order of attributes.
At each level, the matching yields three points:
Higher-level matches take precedence over lower-level matches. For example, the exact match at model level will overweight the wildcard match.
For example, consider the following access request:
{
model: 'order',
property: 'find',
accessType: 'EXECUTE'
}
Assuming the following ACL rules are defined:
[
// Rule #1
{
model: '*',
property: 'find',
accessType: 'EXECUTE',
principalType: 'ROLE',
principalId: '$authenticated',
permission: 'ALLOW'
},
// Rule #2
{
model: 'order',
property: '*',
accessType: '*',
principalType: 'ROLE',
principalId: '$authenticated',
permission: 'ALLOW'
},
// Rule #3
{
model: 'order',
property: 'find',
accessType: '*',
principalType: 'ROLE',
principalId: '$authenticated',
permission: 'DENY'
}
]
The order of ACL rules will be #3, #2, #1. As a result, the request will be rejected as the permission set by rule #3 is ‘DENY’ .
Specify a DEBUG environment variable with value loopback:security:* for the console to log the lookups and checks the server
makes as requests come in, useful to understand things from its perspective.
Do this in your test environment as there may be quite a lot of output.
| Access Type | Property | Verb | End point |
|---|---|---|---|
| READ | find | GET | /model-name-plural |
| findById | GET | /model-name-plural/{id} | |
| exists | HEAD | /model-name-plural/{id} | |
| exists | READ | /model-name-plural/{id}/exists | |
| __get__relationName | GET | /model-name-plural/{id}/relationName | |
| __findById__relationName | GET | /model-name-plural/{id}/relationName/{fk} | |
| __count__relationName | GET | /model-name-plural/{id}/relationname | |
| createChangeStream | GET | /model-name-plural/change-stream | |
| createChangeStream | POST | /model-name-plural/change-stream | |
| count | GET | /model-name-plural/count | |
| findOne | GET | /model-name-plural/findOne | |
| WRITE | upsert | PATCH | /model-name-plural |
| upsert | PUT | /model-name-plural | |
| create | POST | /model-name-plural | |
| updateAttributes | PATCH | /model-name-plural/{id} | |
| updateAttributes | PUT | /model-name-plural/{id} | |
| deleteById | DELETE | /model-name-plural/{id} | |
| replaceById | POST | /model-name-plural/{id}/replace | |
| __create__relationName | POST | /model-name-plural/{id}/relationName | |
| __delete__relationName | DELETE | /model-name-plural/{id}/relationName | |
| __updateById__relationName | PUT | /model-name-plural/{id}/relationName/{fk} | |
| __destroyById__relationName | DELETE | /model-name-plural/{id}/relationName/{fk} | |
| replaceOrCreate | POST | /model-name-plural/replaceOrCreate | |
| updateAll | POST | /model-name-plural/update | |
| upsertWithWhere | POST | /model-name-plural/upsertWithWhere |