Can CASL.js evaluate field restrictions directly against a subject? - casl

Description
My objective is to evaluate a req.body as a CASL.js subject (built with the built-in subject helper) and to determine not only if the user can perform an action against that subject under the predefined conditions, but also if the provided req.body has only the permitted fields defined in the AbilityTuple used for the Ability constructor.
I was almost sure that this was the predetermined functionality of the can() utility until I ran with an unorthodox use of it (not important in this context), where it was obvious that, as long as the conditions matched, the subject evaluated could hold any other fields different than the ones defined in the fields array.
Illustrative example
I have a user 'grant' (a permission) defined in a database JSON:
{
"action": "create",
"subject": "users",
"conditions": {
"idPlatform": 8,
"idRole": 14
},
"fields": [
"idPlatform",
"idRole",
"attributes.**",
"key",
"person.documents.**",
"person.emails.**
]
}
This is passed to the new Ability() constructor along with all the other permissions of a given user, fetched from database. Then an endpoint controller evaluates a user authorization for performing a user creation action before moving on to further methods, using the ability.can() utility in the following way:
async authorize(req) {
const userSubject = subject('users', req.body);
if (!await this.ability.can('create', userSubject)) {
throw new Error('Unauthorized');
}
}
Now suppose a user sends the following body to this endpoint:
idPlatform: 8,
idRole: 14,
key: "myusername",
active: 0,
person: {
documents: [
{
idType: 1,
document: "37548323"
},
{
idType: 3,
document: "20375483231"
},
emails: [
{
idType: 2,
email: "mypersonalemail#gmail.com"
},
{
idType: 3,
email: "myworkemail"
}
],
telephones: [
{
"idType": 1
"telephone": "29445368432"
]
}
As you may see, there are two additional fields apart from those declared in the fields ability object: active and person.telephones. But since the body matches the two conditions defined (idPlatform and idRole), the ability.can() method returns true.
What I'd like is that the ability.can() utility would also evaluate if the provided subject has only the fields allowed for the user, or return false if there are any extra fields. Currently I can achieve that by flattening the subject keys into an strings array:
['idPlatform', 'idRole', 'key', 'active', 'person.documents.idType', 'person.documents.document', ...]
...and iterating through them with the ability.can('create', subject, flattenedField).

Related

Open Policy Agent - Authorizing READ on a list of data

Problem
We've been using OPA to do data authorization of our REST HTTP APIs. We secure our APIs as such
allow {
input.method == "GET"
glob.match(input.path, ["/"], "/department/DEPARTMENT_ID/employee/")
some_rule # Where we check that user can list all employee in the particular deparment/DEPARTMENT_ID based on the ACL of department/DEPARTMENT_ID
}
As seen above, each department has its own ACL we authorize against that for any access to it and its child resources (e.g. employee).
We query this policy via OPA's HTTP API, and we push department/DEPARTMENT_ID's ACL to OPA for it to make a decision. See OPA docs.
However, there's been a new requirement where we have to make an API that has to list all employee that the user has access to.
How could one go about doing this given that the authorization can no longer look at just one ACL? (because multiple employee resources will belong in different department, each with their own ACL).
Potential solution
When listing employee, we could send OPA all the ACLs of each of their department (i.e. the parent), and have OPA authorize based on that. This could be highly inefficient, but I'm not sure if there's any better way. The size of this is also bounded if we paginate the employee listing.
I'm not sure I followed entirely, but given that you have data looking something like the below:
{
"departments": {
"department1": {
"permissions": {
"jane": ["read"]
},
"employees": {
"x": {},
"y": {},
"z": {}
}
},
"department2": {
"permissions": {
"jane": ["read"]
},
"employees": {
"a": {},
"b": {},
"c": {}
}
},
"department3": {
"permissions": {
"eve": ["read"]
},
"employees": {
"bill": {},
"bob": {},
"eve": {}
}
}
}
}
And input looking something like this:
{
"user_id": "jane",
"method": "GET",
"department_id": "department1",
"path": "/department/department1/employee"
}
A policy to query for all listable employees for a user might look something like this:
package play
import future.keywords.in
allow {
input.method == "GET"
glob.match(input.path, ["/"], sprintf("/department/%v/employee", [input.department_id]))
can_read
}
# Where we check that user can list all employee in the particular deparment/DEPARTMENT_ID based on the ACL of department/DEPARTMENT_ID
can_read {
"read" in data.departments[input.department_id].permissions[input.user_id]
}
listable_employees[employee] {
some department in data.departments
"read" in department.permissions[input.user_id]
some employee, _ in department.employees
}
The listable_employees in this case would evaluate to:
[
"a",
"b",
"c",
"x",
"y",
"z"
]
Since user jane has read access to department1 and department2, but not department3.

Redux-Toolkit: how to deal with an entity with compound keys?

I'm trying to use Redux-Toolkit's createEntityAdapter in an entity that has compound keys. For example, my territories entity has a type (municipality, state, biome, etc) and a geocode number. The pair {type, geocode} is the compound key of this entity.
I want to be able to use selectById and other selectors. My first thought was to create an id field that concatenates type, ";" and geocode, but I'm sure there's a better way.
import { createEntityAdapter } from '#reduxjs/toolkit'
const adapter = createEntityAdapter({
// selectId: (item) => ???,
})
const APIresponse = {
data: [
{ type: 'state', geocode: 1, value: 123},
{ type: 'state', geocode: 2, value: 66},
{ type: 'municipality', geocode: 1, value: 77},
{ type: 'municipality', geocode: 2, value: 88},
{ type: 'municipality', geocode: 3, value: 99}
]
}
I'm a Redux maintainer and the person who implemented createEntityAdapter for RTK.
createEntityAdapter does assume that you have some kind of unique ID field in your data. If you don't have a unique ID field from the original data, you've got three options I can think of:
Generate a unique ID for each item when you are processing the API response (but before any "loaded" action is dispatched)
Concatenate together some combination of fields to create a synthesized id field when you are processing the API response
Implement selectId so that it returns a combination of fields each time

Storing data on Firebase to be queried later

We have an application that will store data on Firebase (database) that will then be queried later.
What is the correct format to store the data in.
The example data will be completedGames. They will have data such as:
UserId
TimeToComplete
GameData
Etc...
The query later will then look for all completed games by UserId. We want to ensure the data is collected in the best way possible to query later, rather than refactoring later.
In your case, first off - be sure you have a good reason to use Firebase over Firestore. Once you're confident you should stick with Firebase Realtime Database, look at the below excerpt of documentation. So, you might actually have 2 separate parent nodes, 1 for userId and another for games. Each game node's child is a particular game, which has a child tree of game users (by userId).
Flatten data
structures
If the data is instead split into separate paths, also called
denormalization, it can be efficiently downloaded in separate calls,
as it is needed. Consider this flattened structure:
{
// Chats contains only meta info about each conversation
// stored under the chats's unique ID
"chats": {
"one": {
"title": "Historical Tech Pioneers",
"lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
"timestamp": 1459361875666
},
"two": { ... },
"three": { ... }
},
// Conversation members are easily accessible
// and stored by chat conversation ID
"members": {
// we'll talk about indices like this below
"one": {
"ghopper": true,
"alovelace": true,
"eclarke": true
},
"two": { ... },
"three": { ... }
},
// Messages are separate from data we may want to iterate quickly
// but still easily paginated and queried, and organized by chat
// conversation ID
"messages": {
"one": {
"m1": {
"name": "eclarke",
"message": "The relay seems to be malfunctioning.",
"timestamp": 1459361875337
},
"m2": { ... },
"m3": { ... }
},
"two": { ... },
"three": { ... }
}
}

Pass Alexa Skill slot name to a new intent on emit

When triggering one intent from another, such as
'LaunchRequest': function () {
this.emit('MyCustomIntent')
}
The request object passed to MyCustomIntent is
"request": {
type: 'LaunchRequest',
requestId: '...',
timestamp: '...',
locale: 'en-US'
}
Notice that no intent nor slot information is passed to MyCustomIntent.
However, every MyCustomIntent request that is sent by the Alexa will include
"request": {
"type": "IntentRequest",
...,
"intent": {
"name": "MyCustomIntent",
"slots": {
"MyCustomSlot": {
"name": "MyCustomSlot"
}
}
}
}
This creates a schism in the development process. When attempting to use MyCustomSlot within MyCustomIntent, if intent, intent.slots, and each respective intent.slots.MyCustomSlotdoes not exist, I will then have to use some default value to allow my code to function properly.
This means it will now be necessary for me to maintain my intent schema in both my Interaction Model, and within my Lambda Function. That sounds very messy, and could get out hand quickly when multiple slots and intents are introduced.
My Question
Is there any way to send the default slot values into the emit, so that I know, without a doubt, that I can always guarantee the same basic request object within MyCustomIntent?
You can use session attribute to pass slot values like
'firstIntent': function() {
this.attributes['slot_value1']= value;
alexa.emit('secondIntent');
}

Filtering Firebase lists based nested child keys?

In the Firebase docs for Structuring Data, they give the follow data structure as an example of mapping users to groups.
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// Index Ada's groups in her profile
"groups": {
// the value here doesn't matter, just that the key exists
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}
With that structure, how would I go about querying only groups with a specific member? So only groups where alovelace is a member, for example.
Would I do this with a rule? If so, what would that rule look like?
OrderByChild works in this case - below the query object
angularfire.database.list('.../groups', {
query: {
orderByChild: 'members/alovelace'+,
startAt: true
}
});
Not sure how the performance is compared to the answer by Frank van Puffelen - might be worse even since it's another list query rather than just a few direct object lookups.
That information is already in the data model. Right under /users/alovelace/groups you have a list of the groups she's a member off.
The reason for recommending this model is that it doesn't even require a query to load the list of groups, it just requires a direct-access read of /users/alovelace/groups.

Resources