accesscontrol
accesscontrol copied to clipboard
Filtering inside an array of collections
First of all, thanks for the amazing library. So far it has saved us a ton of time figuring out role and attribute based permissions ourselves. Really appreciate it.
A small hiccup we encountered is filtering inside collections.
Given this object:
{
locality: 'A locality',
street: 'A street',
properties: [
{
name: 'The Oasis',
byLaws: 'The bylaws',
occupants: [
{ name: 'Dan', age: 31 },
{ name: 'Roy', age: 22 }
]
},
{
name: 'Azure',
byLaws: 'The bylaws',
occupants: [
{ name: 'Annie', age: 32 },
{ name: 'Paul', age: 33 }
]
}
]
}
I understand I can filter out street within the root with !street but how do we filter out byLaws within the properties? or how about the age within the occupants of a property?
So far I've tried !*.byLaws, !**.byLaws, !*.*.byLaws and !*.**.byLaws. For age I can imagine it would follow similar patterns for restricting byLaws. Any thoughts on how this can be done?
Thanks again
Thanks.
AccessControl filters a given object or array of objects. It treats arrays in deeper levels as values (not sub-collections) bec. the typical use case is a model fetched from a database.
The data object in your example is more complex (i.e. model with sub-collections). To filter out byLaws property of that sub-collection, you'd do !properties[*].byLaws. Here, * stands for "at any index". However, this type of structure is not yet supported — but in the road map.
I'll mark this as a feature request but cannot guarantee it'll be included in the next version (which will probably be a patch release).
@onury I'm keen to put together a pull request if you can point me in the right direction.
I see that this would be a change on the notation package rather than here.
That'd be great but it'll be a bit tricky. Yes we need to update Notation. I've opened an issue here.
supporting this feature. very useful.. thank you..
@onury, hi! I need the similar feature. I need to filter a collection that is one of my model's property. But I need filter to be able to remove some items from the collection by items' values.
Please, consider the following structure:
{
name: "John",
lastName: "Doe",
age: 23,
files: [
{
name: '1.jpg',
type: 'public'
},
{
name: '2.jpg',
type: 'private'
},
{
name: '3.jpg',
type: 'public'
}
]
}
As I understand, you're discussing here the issue with filtering the files array. It is not possible to remove, for example, name property from the objects that are in the files array.
In my case, I need to filter this files array in another way: I'd like to remove all the objects that have the property type equals to private. It would be great to take this into account during developing v3.
I didn't find the opened issue that describes this case.
Thanks!
... It is not possible to remove, for example,
nameproperty from the objects that are in thefilesarray.
Actually there is a dirty workaround if you define a file (sub) resource.
// grant permissions
ac.grant('user')
.read('person') // attrs: ['name', 'lastName', 'age', 'files']
.read('file', ['*', '!name']);
// Check permissions and filter
let filteredData;
let permission = ac.can('user').read('person');
if (permission.granted) {
filteredData = permission.filter(data);
// if we still have .files property after first filter:
if (filteredData.files) {
// checking kind of a sub-resource
permission = ac.can('user').read('file');
if (permission.granted) filteredData.files = permission.filter(filteredData.files);
}
}
This multi-filtered data will output:
{
name: "John",
lastName: "Doe",
age: 23,
files: [
{ type: 'public' },
{ type: 'private' },
{ type: 'public' }
]
}
In my case, I need to filter this
filesarray in another way: I'd like to remove all the objects that have the propertytypeequals toprivate.
@turakvlad filter uses a glob notation list to exclude/include properties. Each negated notation will be removed at that level. But what you want is to remove a parent object conditionally by its property. I think this would complicate things too much. It's kind of, like data validation and actually it is not what filter is for.
It seems your use case does not enforce any role permission checks for this. So you may want to handle this within your data models. e.g. if using a database; SELECT ... WHERE type <> 'private'.