Simple role and service method permissions for Feathers
Note: This module implements a hook simple role and service method based permissions checked against the permissions in a user (entity) object. More complex requirements can already be implemented as custom Feathers hooks. See here and here for more information.
Installation
npm install feathers-permissions --save
Important: The
feathers-permissionshook should be used after theauthenticate()hook from @feathersjs/authentication.
Simple example
The following example will limit all messages service calls to users that have admin in their roles:
const feathers = require('@feathersjs/feathers'); const memory = require('feathers-memory'); const checkPermissions = require('feathers-permissions'); const app = feathers(); app.use('/messages', memory()); app.service('messages').hooks({ before: [ authenticate('jwt'), checkPermissions({ roles: [ 'admin' ] }) ] }); // User from the database const user = { email: 'someuser@example.com', permissions: [ 'admin' ] }
Documentation
Feathers permissions allows you to grant and manage permissions in a flexible nature based on role and service method. Each object that requires permissions must have an array or a comma separated string of permissions stored on it (typically in your database).
Options
The following options are available:
roles- A list of permission roles to check or a function that takes the hookcontextand returns a list of roles. Can be a comma separated string of roles or an array of roles.entity(default:user) - The name of the entity (params[entity])field(default:permissions) - The name of the permissions field on the entity. May be dot separated to access nested fields.error- If set tofalsewill not throw aForbiddenerror but instead setparams.permittedtotrueorfalse. Useful for chaining permission hooks.
Permission format
The list of permissions will be obtained from params[entity] and field. It can be a comma separate list or an array of permissions in the following format:
*- Allow everything${role}or${role}:*- Allow every service method (find,get,create,update,patch,remove) forrole*:${method}- Allowmethodservice method for any role${role}:${method}- Allowmethodservice method forrole
This means the following use of feathers-permissions:
app.service('messages').hooks({ before: checkPermissions({ roles: [ 'admin', 'user' ] }) });
Will allow user permissions containing *, admin:*, user:* and the service method that is being called (e.g. admin:create or user:find and *:create and *:find).
The following will create a dynamic permission based on the hook context.path:
app.service('messages').hooks({ before: checkPermissions({ roles: context => { return [ 'admin', context.path ]; } }) });
Permissions can also be assembled asynchronously:
app.service('messages').hooks({ before: checkPermissions({ roles: async context => { const { user } = context.params; const roles = await app.service('roles').find({ query: { userId: user._id } }); return roles.data; } }) });
Conditionally restricting permissions
To conditionally either allow access by roles or otherwise restrict to the current user, a combination of feathers-permissions - setting the error option to false - feathers-authentication-hooks and feathers-hooks-common#iff (checking for params.permitted) can be used:
app.service('messages').hooks({ before: { find: [ checkPermissions({ roles: ['super_admin', 'admin'], field: 'roles', error: false }), iff(context => !context.params.permitted, setField({ from: 'params.user._id', as: 'params.query.userId' }) ) ] } });
More examples
const feathers = require('@feathersjs/feathers'); const memory = require('feathers-memory'); const checkPermissions = require('feathers-permissions'); const app = feathers(); app.use('/messages', memory()); app.service('messages').hooks({ before: checkPermissions({ roles: [ 'admin', 'messages' ] }) }); // User from the database (e.g. added via @feathersjs/authentication) const user = { email: 'someuser@example.com', permissions: [ 'messages:find', 'messages:get' ] // Also possible permissions: 'messages:find,messages:get' } const admin = { email: 'someuser@example.com', permissions: [ 'admin:*' ] } // Will pass app.service('messages').find({ user }); // Will fail app.service('messages').create({ user }); // Will pass app.service('messages').create({ provider: 'rest', // this will be set automatically by external calls user: admin });
License
Copyright (c) 2019
Licensed under the MIT license.