Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

sails-permissions

Package Overview
Dependencies
Maintainers
3
Versions
83
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

sails-permissions - npm Package Compare versions

Comparing version 1.3.1 to 1.3.10-beta

1

api/hooks/sails-permissions.js

@@ -106,2 +106,3 @@ var permissionPolicies = [

if (model.autoCreatedBy === false) return;
if (model.meta.junctionTable) return;

@@ -108,0 +109,0 @@ _.defaults(model.attributes, {

12

api/models/Permission.js

@@ -29,13 +29,3 @@ /**

index: true,
notNull: true,
/**
* TODO remove enum and support permissions based on all controller
* actions, including custom ones
*/
enum: [
'create',
'read',
'update',
'delete'
]
notNull: true
},

@@ -42,0 +32,0 @@

@@ -16,3 +16,3 @@ /**

var action = PermissionService.getMethod(req.method);
var action = PermissionService.getAction(req.options);

@@ -77,3 +77,2 @@ var body = req.body || req.query;

var user = req.owner;
var method = PermissionService.getMethod(req);
var isResponseArray = _.isArray(_data);

@@ -80,0 +79,0 @@

@@ -1,3 +0,1 @@

var actionUtil = require('sails/lib/hooks/blueprints/actionUtil');
/**

@@ -8,19 +6,15 @@ * Query the Model that is being acted upon, and set it on the req object.

var modelCache = sails.hooks['sails-permissions']._modelCache;
req.options.modelIdentity = actionUtil.parseModel(req).identity;
if (_.isEmpty(req.options.modelIdentity)) {
return next();
}
req.options.modelDefinition = sails.models[req.options.modelIdentity];
req.model = modelCache[req.options.modelIdentity];
req.options.modelDefinition = sails.models[req.options.model];
req.model = modelCache[req.options.model];
if (_.isObject(req.model) && !_.isEmpty(req.model.id)) {
if (_.isObject(req.model) && !_.isUndefined(req.model.id)) {
return next();
}
sails.log.warn('Model [', req.options.modelIdentity, '] not found in model cache');
sails.log.warn('Model [', req.options.model, '] not found in model cache');
// if the model is not found in the cache for some reason, get it from the database
Model.findOne({ identity: req.options.modelIdentity })
Model.findOne({ identity: req.options.model})
.then(function (model) {

@@ -31,6 +25,6 @@ if (!_.isObject(model)) {

if (!sails.config.permissions.allowUnknownModelDefinition) {
return next(new Error('Model definition not found: '+ req.options.modelIdentity));
return next(new Error('Model definition not found: '+ req.options.model));
}
else {
model = sails.models[req.options.modelIdentity];
model = sails.models[req.options.model];
}

@@ -37,0 +31,0 @@ }

@@ -23,7 +23,6 @@ var Promise = require('bluebird');

module.exports = function (req, res, next) {
var options = {
var options = _.defaults({
model: req.model,
method: req.method,
user: req.user
};
}, req.options);

@@ -38,3 +37,3 @@ if (req.options.unknownModel) {

sails.log.silly('PermissionPolicy:', permissions.length, 'permissions grant',
req.method, 'on', req.model.name, 'for', req.user.username);
PermissionService.getAction(options), 'on', req.model.name, 'for', req.user.username);

@@ -50,47 +49,1 @@ if (!permissions || permissions.length === 0) {

};
function bindResponsePolicy (req, res) {
res._ok = res.ok;
res.ok = _.bind(responsePolicy, {
req: req,
res: res
});
}
function responsePolicy (_data, options) {
var req = this.req;
var res = this.res;
var user = req.owner;
var method = PermissionService.getMethod(req);
var data = _.isArray(_data) ? _data : [_data];
//sails.log('data', _data);
//sails.log('options', options);
// TODO search populated associations
Promise.bind(this)
.map(data, function (object) {
return user.getOwnershipRelation(data);
})
.then(function (results) {
//sails.log('results', results);
var permitted = _.filter(results, function (result) {
return _.any(req.permissions, function (permission) {
return permission.permits(result.relation, method);
});
});
if (permitted.length === 0) {
//sails.log('permitted.length === 0');
return res.send(404);
}
else if (_.isArray(_data)) {
return res._ok(permitted, options);
}
else {
res._ok(permitted[0], options);
}
});
}

@@ -12,3 +12,3 @@ /**

var relations = _.groupBy(permissions, 'relation');
var action = PermissionService.getMethod(req.method);
var action = PermissionService.getAction(req.options);

@@ -15,0 +15,0 @@ // continue if there exist role Permissions which grant the asserted privilege

@@ -8,2 +8,12 @@ var Promise = require('bluebird');

};
var actionMap = {
create: 'create',
find: 'read',
findOne: 'read',
update: 'update',
destroy: 'delete',
populate: 'read',
add: 'update',
remove: 'update'
};

@@ -31,4 +41,2 @@ var findRecords = require('sails/lib/hooks/blueprints/actions/find');

return function (object) {
//sails.log('object', object);
//sails.log('object.owner: ', object.owner, ', owner:', owner);
return object.owner !== owner;

@@ -51,3 +59,6 @@ };

ok: resolve,
serverError: reject
serverError: reject,
// this isn't perfect, since it returns a 500 error instead of a 404 error
// but it is better than crashing the app when a record doesn't exist
notFound: reject
});

@@ -61,3 +72,3 @@ });

*
* @param options.method
* @param options.action
* @param options.model

@@ -67,3 +78,3 @@ * @param options.user

findModelPermissions: function (options) {
var action = PermissionService.getMethod(options.method);
var action = PermissionService.getAction(options);
var permissionCriteria = {

@@ -164,3 +175,3 @@ model: options.model.id,

return [
'User', options.user.email, 'is not permitted to', options.method, options.model.globalId
'User', options.user.email, 'is not permitted to', options.action, options.model.identity
].join(' ');

@@ -170,6 +181,14 @@ },

/**
* Given an action, return the CRUD method it maps to.
* Given a request, return the CRUD action or controller action it maps
* to.
*
* @param req.options
*
* If a standard blueprint action, then translate that blueprint action into a
* CRUD action. If a custom controller action, then return the name of that action as-is.
*/
getMethod: function (method) {
return methodMap[method];
getAction: function (options) {
var action = actionMap[options.action] || options.action;
return action;
},

@@ -229,3 +248,6 @@

* @param options {permission object, or array of permissions objects}
* @param options.role {string} - the role name that the permission is associated with
* @param options.role {string} - the role name that the permission is associated with,
* either this or user should be supplied, but not both
* @param options.user {string} - the user than that the permission is associated with,
* either this or role should be supplied, but not both
* @param options.model {string} - the model name that the permission is associated with

@@ -244,9 +266,14 @@ * @param options.action {string} - the http action that the permission allows

var ok = Promise.map(permissions, function (permission) {
return Model.findOne({name: permission.model})
.then(function (model) {
var findRole = permission.role ? Role.findOne({name: permission.role}) : null;
var findUser = permission.user ? User.findOne({username: permission.user}) : null;
return Promise.all([findRole, findUser, Model.findOne({name: permission.model})])
.spread(function (role, user, model) {
permission.model = model.id;
return Role.findOne({name: permission.role})
.then(function (role) {
if (role && role.id) {
permission.role = role.id;
});
} else if (user && user.id) {
permission.user = user.id;
} else {
return Promise.reject(new Error('no role or user specified'));
}
});

@@ -278,4 +305,4 @@ });

return Role.findOne({name: rolename}).populate('users').then(function (role) {
User.find({username: usernames}).then(function (users) {
role.users.add(users);
return User.find({username: usernames}).then(function (users) {
role.users.add(_.pluck(users, 'id'));
return role.save();

@@ -314,3 +341,4 @@ });

* @param options
* @param options.role {string} - the name of the role related to the permission
* @param options.role {string} - the name of the role related to the permission. This, or options.user should be set, but not both.
* @param options.user {string} - the name of the user related to the permission. This, or options.role should be set, but not both.
* @param options.model {string} - the name of the model for the permission

@@ -321,11 +349,23 @@ * @param options.action {string} - the name of the action for the permission

revoke: function (options) {
var ok = Promise.all([Role.findOne({name: options.role}), Model.findOne({name: options.model})]);
ok = ok.then(function (result) {
var role = result[0];
var model = result[1];
return Permission.destroy({role: role.id,
var findRole = options.role ? Role.findOne({name: options.role}) : null;
var findUser = options.user ? User.findOne({username: options.user}) : null;
var ok = Promise.all([findRole, findUser, Model.findOne({name: options.model})]);
ok = ok.spread(function (role, user, model) {
var query = {
model: model.id,
action: options.action,
relation: options.relation
});
};
if (role && role.id) {
query.role = role.id;
} else if (user && user.id) {
query.user = user.id;
} else {
return Promise.reject(new Error('You must provide either a user or role to revoke the permission from'));
}
return Permission.destroy(query);
});

@@ -332,0 +372,0 @@

{
"name": "sails-permissions",
"version": "1.3.1",
"version": "1.3.10-beta",
"description": "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.",

@@ -42,3 +42,3 @@ "main": "api/hooks/sails-permissions.js",

"request": "^2.58.0",
"sails": ">0.10.0",
"sails": "balderdashy/sails",
"sails-disk": "^0.10.7",

@@ -56,5 +56,2 @@ "supertest": "^0.15.0"

},
"peerDependencies": {
"sails-auth": ">=1.3"
},
"engines": {

@@ -61,0 +58,0 @@ "node": ">= 0.10",

@@ -80,34 +80,2 @@ var assert = require('assert');

describe('#getMethod()', function () {
it ('should return \'create\' if POST request', function(done) {
assert.equal(sails.services.permissionservice.getMethod('POST'), 'create');
done();
});
it ('should return \'update\' if PUT request', function(done) {
assert.equal(sails.services.permissionservice.getMethod('PUT'), 'update');
done();
});
it ('should return \'read\' if GET request', function(done) {
assert.equal(sails.services.permissionservice.getMethod('GET'), 'read');
done();
});
it ('should return \'delete\' if DELETE request', function(done) {
assert.equal(sails.services.permissionservice.getMethod('DELETE'), 'delete');
done();
});
});
describe('#hasPassingCriteria()', function () {

@@ -226,4 +194,4 @@

assert(role && role.id);
done();
});
})
.done(done, done);
});

@@ -255,6 +223,35 @@

assert(permission && permission.id);
done();
});
})
.done(done, done);
});
it ('should grant a permission directly to a user', function (done) {
var permissionModelId;
// find any existing permission for this action, and delete it
Model.findOne({name: 'Permission'}).then(function (permissionModel) {
permissionModelId = permissionModel.id;
return Permission.destroy({action: 'create', model: permissionModelId, relation: 'role'});
})
.then(function (destroyed) {
// make sure we actually destroyed it
return Permission.find({action: 'create', relation: 'role', model: permissionModelId });
})
.then(function (permission) {
assert.equal(permission.length, 0);
// create a new permission
var newPermissions = [{user: 'admin', model: 'Permission', action: 'create', relation: 'role', criteria: { where: { x: 1}, blacklist: ['y'] }},
{user: 'admin', model: 'Role', action: 'update', relation: 'role', criteria: { where: { x: 1}, blacklist: ['y'] }}];
return sails.services.permissionservice.grant(newPermissions);
})
.then(function (perm) {
// verify that it was created
return Permission.findOne({action: 'create', relation: 'role', model: permissionModelId})
})
.then(function (permission) {
assert(permission && permission.id);
})
.done(done, done);
});
it ('should revoke a permission', function (done) {

@@ -265,7 +262,7 @@

permissionModelId = permissionModel.id;
return Permission.find({action: 'create', relation: 'role', model: permissionModelId });
return Permission.find({action: 'create', relation: 'role', model: permissionModelId});
})
.then(function (permission) {
assert.equal(permission.length, 1);
return sails.services.permissionservice.revoke({role: 'fakeRole', model: 'Permission', relation: 'role', action: 'create'});
return sails.services.permissionservice.revoke({user: 'admin', model: 'Permission', relation: 'role', action: 'create'});
})

@@ -277,3 +274,33 @@ .then(function () {

assert.equal(permission.length, 0);
done();
})
.done(done, done);
});
it ('should not revoke a permission if no user or role is supplied', function (done) {
var newPermissions = [{user: 'admin', model: 'Permission', action: 'create', relation: 'role', criteria: { where: { x: 1}, blacklist: ['y'] }},
{user: 'admin', model: 'Role', action: 'update', relation: 'role', criteria: { where: { x: 1}, blacklist: ['y'] }}];
return sails.services.permissionservice.grant(newPermissions)
.then(function() {
// make sure there is already an existing permission for this case
Model.findOne({name: 'Permission'}).then(function (permissionModel) {
permissionModelId = permissionModel.id;
return Permission.find({action: 'create', relation: 'role', model: permissionModelId});
})
.then(function (permission) {
assert.equal(permission.length, 1);
return sails.services.permissionservice.revoke({model: 'Permission', relation: 'role', action: 'create'});
})
.catch(function (err) {
assert.equal(err.message, 'You must provide either a user or role to revoke the permission from');
})
.then(function () {
return Permission.find({action: 'create', relation: 'role', model: permissionModelId });
})
.then(function (permission) {
assert.equal(permission.length, 1);
})
.done(done, done);
});

@@ -283,2 +310,25 @@ });

});
describe('#getAction', function () {
describe('CRUD actions', function () {
it('@findone: should return the "read" action', function () {
assert.equal(PermissionService.getAction({ action: 'findOne' }), 'read');
});
it('@find: should return the "read" action', function () {
assert.equal(PermissionService.getAction({ action: 'find' }), 'read');
});
it('@create: should return the "create" action', function () {
assert.equal(PermissionService.getAction({ action: 'create' }), 'create');
});
});
describe('custom actions', function () {
it('@upload: should return the "upload" action', function () {
assert.equal(PermissionService.getAction({ action: 'upload' }), 'upload');
});
it('@download: should return the "download" action', function () {
assert.equal(PermissionService.getAction({ action: 'upload' }), 'upload');
});
});
})
//TODO: add unit tests for #findTargetObjects()

@@ -285,0 +335,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc