Open phishy opened 9 years ago
:+1:
+1
+1
I see that it is not implemented yet, https://github.com/tjwebb/sails-permissions/blob/master/api/models/Permission.js#L27
@phishy this is now supported as of #69. Any help testing it out is appreciated.
I would be happy to test but do you have any info on how to use it?
this is awesome!
Ok just trying the permission criteria but it doesn't seem to have any effect.
I have set the permission using
PermissionService.grant({
role: 'public',
model: 'Product',
action: 'read',
criteria: {
blacklist: ['inventory']
}
});
where inventory is an association to a model
I was hoping that any public user would then not see the inventory attribute which is populated in my controller.
I've also tried it on another field that isn't an association and the attribute is still displayed in the response.
@dottodot Are you using a blueprint controller or a custom controller for the Product/read request? Blacklist filtering only works if the result is sent back with res.ok
, so if you are using a custom controller, make sure you send the response via res.ok
.
I have also noticed some unusual behavior with the 'public' role. I will see if I can reproduce this when I have a few minutes.
Yes I'm using a custom controller, I've just changed it to res.ok
and that's made no difference and I've tried on the registered role and it's still the same.
I'm wondering if the 'where' option is required for it to work but not sure what you'd use field that can be any value.
No, the 'where' option should not be required for it to work. Let me have a look at it now.
Have you checked that the Permission looks correct in sails console, via Permission.find()?
OK below is the output of Permission.find() but I'm not sure what to look for to know if it's right.
{ _context:
{ connections: { mongo_development: [Object] },
waterline:
{ _collections: [Object],
_connections: {},
collections: [Object],
connections: [Object],
schema: [Object] },
adapter:
{ connections: [Object],
dictionary: [Object],
query: [Circular],
collection: 'permission',
identity: 'permission' },
_attributes:
{ model: [Object],
action: [Object],
relation: [Object],
role: [Object],
user: [Object],
criteria: [Object],
id: [Object],
createdAt: [Object],
updatedAt: [Object] },
defaults:
{ migrate: 'safe',
schema: true,
connection: 'mongo_development' },
_cast: { _types: [Object] },
_schema:
{ context: [Circular],
schema: [Object],
hasSchema: true },
_validator:
{ validations: [Object],
reservedProperties: [Object] },
_callbacks:
{ beforeValidate: [Object],
afterValidate: [Object],
beforeUpdate: [Object],
afterUpdate: [Object],
beforeCreate: [Object],
afterCreate: [Object],
beforeDestroy: [Object],
afterDestroy: [Object] },
_instanceMethods: {},
hasSchema: true,
migrate: 'safe',
_model: [Function: bound],
primaryKey: 'id',
_transformer: { _transformations: {} },
adapterDictionary:
{ pkFormat: 'mongo_development',
syncable: 'mongo_development',
defaults: 'mongo_development',
registerConnection: 'mongo_development',
teardown: 'mongo_development',
describe: 'mongo_development',
define: 'mongo_development',
drop: 'mongo_development',
native: 'mongo_development',
mongo: 'mongo_development',
create: 'mongo_development',
createEach: 'mongo_development',
find: 'mongo_development',
update: 'mongo_development',
destroy: 'mongo_development',
count: 'mongo_development',
join: 'mongo_development',
stream: 'mongo_development',
identity: 'mongo_development' },
pkFormat: 'string',
syncable: true,
registerConnection: [Function: bound],
teardown: [Function: bound],
define: [Function: bound],
native: [Function: bound],
mongo: { objectId: [Function] },
findOneByAction: [Function: bound],
findOneByActionIn: [Function: bound],
findOneByActionLike: [Function: bound],
findByAction: [Function: bound],
findByActionIn: [Function: bound],
findByActionLike: [Function: bound],
countByAction: [Function: bound],
countByActionIn: [Function: bound],
countByActionLike: [Function: bound],
actionStartsWith: [Function: bound],
actionContains: [Function: bound],
actionEndsWith: [Function: bound],
findOneByRelation: [Function: bound],
findOneByRelationIn: [Function: bound],
findOneByRelationLike: [Function: bound],
findByRelation: [Function: bound],
findByRelationIn: [Function: bound],
findByRelationLike: [Function: bound],
countByRelation: [Function: bound],
countByRelationIn: [Function: bound],
countByRelationLike: [Function: bound],
relationStartsWith: [Function: bound],
relationContains: [Function: bound],
relationEndsWith: [Function: bound],
findOneById: [Function: bound],
findOneByIdIn: [Function: bound],
findOneByIdLike: [Function: bound],
findById: [Function: bound],
findByIdIn: [Function: bound],
findByIdLike: [Function: bound],
countById: [Function: bound],
countByIdIn: [Function: bound],
countByIdLike: [Function: bound],
idStartsWith: [Function: bound],
idContains: [Function: bound],
idEndsWith: [Function: bound],
findOneByCreatedAt: [Function: bound],
findOneByCreatedAtIn: [Function: bound],
findOneByCreatedAtLike: [Function: bound],
findByCreatedAt: [Function: bound],
findByCreatedAtIn: [Function: bound],
findByCreatedAtLike: [Function: bound],
countByCreatedAt: [Function: bound],
countByCreatedAtIn: [Function: bound],
countByCreatedAtLike: [Function: bound],
createdAtStartsWith: [Function: bound],
createdAtContains: [Function: bound],
createdAtEndsWith: [Function: bound],
findOneByUpdatedAt: [Function: bound],
findOneByUpdatedAtIn: [Function: bound],
findOneByUpdatedAtLike: [Function: bound],
findByUpdatedAt: [Function: bound],
findByUpdatedAtIn: [Function: bound],
findByUpdatedAtLike: [Function: bound],
countByUpdatedAt: [Function: bound],
countByUpdatedAtIn: [Function: bound],
countByUpdatedAtLike: [Function: bound],
updatedAtStartsWith: [Function: bound],
updatedAtContains: [Function: bound],
updatedAtEndsWith: [Function: bound],
definition:
{ model: [Object],
action: [Object],
relation: [Object],
role: [Object],
user: [Object],
id: [Object],
createdAt: [Object],
updatedAt: [Object] },
meta: { junctionTable: false },
alter: [Function: bound],
buildDynamicFinders: [Function: bound],
constructor: [Function: bound],
contains: [Function: bound],
count: [Function: bound],
create: [Function: bound],
createEach: [Function: bound],
describe: [Function: bound],
destroy: [Function: bound],
drop: [Function: bound],
endsWith: [Function: bound],
find: [Function: bound],
findAll: [Function: bound],
findLike: [Function: bound],
findOne: [Function: bound],
findOneLike: [Function: bound],
findOrCreate: [Function: bound],
findOrCreateEach: [Function: bound],
generateAssociationFinders: [Function: bound],
generateDynamicFinder: [Function: bound],
join: [Function: bound],
select: [Function: bound],
startsWith: [Function: bound],
stream: [Function: bound],
sync: [Function: bound],
update: [Function: bound],
validate: [Function: bound],
where: [Function: bound],
associations:
[ [Object],
[Object],
[Object],
[Object] ],
broadcast: [Function],
getAllContexts: [Function],
message: [Function],
publish: [Function: bound],
pluralize: [Function],
room: [Function: bound],
classRoom: [Function],
_classRoom: [Function],
subscribers: [Function],
watchers: [Function],
subscribe: [Function: bound],
unsubscribe: [Function: bound],
publishUpdate: [Function: bound],
publishDestroy: [Function: bound],
publishAdd: [Function: bound],
publishRemove: [Function: bound],
publishCreate: [Function: bound],
watch: [Function: bound],
unwatch: [Function: bound],
introduce: [Function: bound],
retire: [Function: bound],
autosubscribe: true },
_method: [Function: bound],
_criteria: { where: null },
_values: null,
_deferred: null }
That method returns a promise, which is what you are seeing there. Try this:
Role.find({name: 'public'}).exec(console.log)
Take the id from the role and plug it into this query:
Permission.find({role: theIdFromRole, action: 'read'}).exec(console.log)
Actually the second query should look like this:
Permission.find({role: theIdFromRole, action: 'read'}).populate('criteria').exec(console.log)
So this is the response...
[
{
criteria: [
{
blacklist: [
'inventory'
],
permission: '55a7b4efeed630c81a14e659',
createdAt: '2015-07-16T13: 43: 11.118Z',
updatedAt: '2015-07-16T13: 43: 11.118Z',
id: '55a7b4efeed630c81a14e65a'
}
],
model: '55a7b4c0a2e56fae1a48666b',
role: '55a7b4c0a2e56fae1a486679',
action: 'read',
relation: 'role',
createdAt: '2015-07-16T13: 43: 11.111Z',
updatedAt: '2015-07-16T13: 43: 11.114Z',
id: '55a7b4efeed630c81a14e659'
}
]
That looks good. When you tried on the registered role, did you make the request with a registered (logged in) user?
Yes I logged in as the registered user then made the request.
But just found the cause. It was a combination of me missing adding the Criteria Policy to policy.js (sorry about that) and then I was also sending my response as below for pagination purposes.
res.ok({
items: products,
totalRecords: count
});
It would seem the criteria policy will only work if sent as
res.ok(products)
Is there any way around this? Or is it just a case of me creating a custom Criteria Policy to meet my needs?
Sorry I didn't respond to this earlier. You are correct, the 'read' filtering only works if you send it like res.ok(products)
The code that does it is here: https://github.com/tjwebb/sails-permissions/blob/master/api/policies/CriteriaPolicy.js#L84
I will keep thinking about how to extend the read attribute filtering.
@phishy there is a bit of documentation at the bottom of this page about row-level security: https://github.com/tjwebb/sails-permissions/wiki/Managing-Roles-and-Permissions
@dottodot Did you ever manage to find a solution for this? I'm having the same problem.
@khchan Sorry which problem are you referring as I mentioned a couple I was having.
@dottodot ah should have been more specific. I was wondering if you found a solution for res.ok to have a pagination total or did you end up having to create a custom criteria policy?
@khchan I did it by sending the count in a header like this.
User.count().then(function(count) {
User.find().paginate({
page: req.param('page'),
limit: req.param('limit')
}).then(function(users) {
res.set('Access-Control-Expose-Headers', 'X-Total-Count');
res.set('X-Total-Count', count);
res.ok(users);
});
});
The README mentions row-level security. Is there any documentation for how this works?