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-permissions
hook 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 hookcontext
and 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 tofalse
will not throw aForbidden
error but instead setparams.permitted
totrue
orfalse
. 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}
- Allowmethod
service method for any role${role}:${method}
- Allowmethod
service 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.