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.
npm install feathers-permissions --save
Important: The
feathers-permissionshook should be used after theauthenticate()hook from @feathersjs/authentication.
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: '[email protected]',
permissions: [ 'admin' ]
}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).
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.
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;
}
})
});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'
})
)
]
}
});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: '[email protected]',
permissions: [ 'messages:find', 'messages:get' ]
// Also possible
permissions: 'messages:find,messages:get'
}
const admin = {
email: '[email protected]',
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
});Copyright (c) 2019
Licensed under the MIT license.