UNPKG

@inspire-platform/sails-hook-permissions

Version:

Comprehensive user permissions and entitlements system for sails.js and Waterline. Supports user authentication with passport.js, role-based permissioning, object ownership, and row-level security.

261 lines (215 loc) 8.24 kB
'use strict'; var _ = require('lodash'); var helpers = { /** * Return all tracked models. * * @param {Object} criteria * @param {Array} criteria.select Fields to select, defaults to: id, name, identity. * @return {Promise} */ findModels: function findModels() { var criteria = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; if (false === 'select' in criteria) { criteria.select = ['id', 'name', 'identity']; } // find models using criteria return Model.find(criteria); }, /** * Lookup all permissions granted to one user (user, and role relations). * * @param {Object} user User object * @param {Object} options * @param {String} options.model * @param {String} options.action * @param {Array} options.populate * @returns {Promise} */ findUserPermissions: function findUserPermissions() { var user = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; // find user var findOneUser = User.findOne(user.id); // populate roles and permissions findOneUser.populate('roles', { active: true }); // exec return findOneUser.then(function (user) { // find all permissions assigned directly to user, or via role var permCriteria = { or: [{ user: user.id }] }; // push every role onto `or` _.forEach(user.roles, function (role) { permCriteria.or.push({ role: role.id }); }); // specific model and/or action id? ['model', 'action'].forEach(function (property) { if (true === property in options && (true === _.isFinite(options[property]) || true === _.isString(options[property]) && 1 <= options[property].length)) { // yep, set it permCriteria[property] = options[property]; } }); // specific model (object)? if (true === _.has(options, ['model', 'id'])) { permCriteria.model = options.model.id; } // init perm find promise var findPerm = Permission.find(permCriteria); // populate any permissions associations? if (true === 'populate' in options && true === Array.isArray(options.populate) && 1 <= options.populate.length) { // yes, loop them options.populate.forEach(function (assoc) { // is object? if (true === _.isPlainObject(assoc)) { // yes, has criteria? if ('criteria' in assoc) { // yes, use it! findPerm.populate(assoc.model, assoc.criteria); } else { // no criteria, just model findPerm.populate(assoc.model); } } else { // nothing fancy findPerm.populate(assoc); } }); } // finally return promise return findPerm.then(function (permissions) { // options for applying base permissions var baseOpts = { model: permCriteria.model, action: permCriteria.action }; // apply base permissions helpers.applyUserBasePermissions(user, permissions, baseOpts); // all done return permissions; }); }); }, /** * Apply all configured base permissions to array of permissions. * * This method mutates the permissions array (append only). * * @param {Object} user User object * @param {Array} permissions Array of permissions (mutated) * @param {Object} options * @param {String} options.model * @param {String} options.action */ applyUserBasePermissions: function applyUserBasePermissions(user, permissions, options) { // might need to inject some base perms if (_.has(sails.config, 'permissions.basePermissions')) { (function () { // copy ref to save typing ;) var basePerms = sails.config.permissions.basePermissions; // loop each base type grouping ['self', 'global'].forEach(function (baseType) { // have some permissions? if (baseType in basePerms && _.isArray(basePerms[baseType])) { // yep, let's go var basePermsType = basePerms[baseType]; // loop all of them basePermsType.forEach(function (permOfType) { // must be an object if (true === _.isPlainObject(permOfType) && true === 'model' in permOfType && true === 'action' in permOfType) { var _ret2 = (function () { var modelId = sails.hooks.permissions._modelCache[permOfType.model].id; // specific model and/or action? if (true === 'model' in options && options.model !== modelId || true === 'action' in options && options.action !== permOfType.action) { // skip to next permission return { v: undefined }; }; // forge permission var basePermission = { _isBase: true, model: modelId, action: permOfType.action, relation: 'user', user: user.id, criteria: [], objectFilters: [] }; // handle criteria and/or object filters ['criteria', 'objectFilters'].forEach(function (prop) { if (true === prop in permOfType && true === _.isArray(permOfType[prop])) { basePermission[prop] = permOfType[prop]; } }); // is self type? if ('self' === baseType) { // yes, add object filter for user id basePermission.objectFilters.push({ objectId: user.id }); } // append base permission permissions.push(basePermission); })(); if (typeof _ret2 === 'object') return _ret2.v; } }); } }); })(); } }, /** * Lookup all features granted to one user (user, and role relations). * * @param {Object} user User object * @param {Object} options * @returns {Promise} */ findUserFeatures: function findUserFeatures() { var user = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; // find user var findOneUser = User.findOne(user.id); // populate user roles and features findOneUser.populate('roles', { active: true }).populate('features', { active: true }); // exec return findOneUser.then(function (user) { // user belongs to any roles? if (user.roles.length >= 1) { var _ret3 = (function () { // find all features assigned to user via role. // this requires a second role query with features populated. var extRoleCriteria = { or: [] }; // push every role onto `or` _.forEach(user.roles, function (role) { extRoleCriteria.or.push({ id: role.id }); }); // init roles + features find promise var findExtRoles = Role.find(extRoleCriteria).populate('features', { active: true }); // try to find role features return { v: findExtRoles.then(function (roles) { // need one flat array (including directly assigned) var features = Array.prototype.concat(user.features, _.flatten(_.map(roles, function (role) { return role.features; }))); // unique by id return _.uniq(features, 'id'); }) }; })(); if (typeof _ret3 === 'object') return _ret3.v; } else { // no roles, return only directly assigned user features return user.features; } }); } }; module.exports = helpers;