@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
JavaScript
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;
;