Nabil
09/09/2022, 8:28 PMAlex Olivier (Cerbos)
09/09/2022, 8:40 PMNabil
09/10/2022, 4:09 PMAlex Olivier (Cerbos)
09/12/2022, 9:19 AMNabil
09/13/2022, 4:16 PMcontact
which has an owner, department, etc. The /contacts
endpoint uses a query plan to build a filter for grabbing contacts for a user. What would you change about your cerbos policy for a contact
if a contact
didn't have a singular ownerId
but instead had a list of user ids that had ownership (or access) to it?
References:
• https://github.com/cerbos/express-prisma-cerbos/blob/main/cerbos/policies/common_roles.yaml
• https://github.com/cerbos/express-prisma-cerbos/blob/main/src/index.ts#L75userId
to resourceId
and then I join against that table for every single query by a user for some list of resources. This is very painful and what cerbos looks to assist with using query plans that return filters. I hope this makes sense. Thank you for your time and consideration!@cerbos/orm-prisma
adaptertenant
that has an id, name, and a list of users who have access to that tenant.
• I represent that list of users with another resource (and db table) called tenant_user
which has a _tenant_id_ and _user_id_. The _tenant_id_ is a foreign key back into the tenant
table.
• I define a cerbos policy for the tenant_user
resource with a rule like this:
- actions:
- read
effect: EFFECT_ALLOW
roles:
- user
condition:
match:
expr: request.resource.attr.user_id == request.principal.id
This results in an AST that I can translate into a WHERE clause (using your @cerbos/orm-prisma
module in this case) and attach to my query.
This situation is kinda hitting on my concern from my initial question in this thread. The database schema's design seems like it will invariably end up being coupled to how the app uses cerbos. Let's say I need to change what it means for a user to have access to a tenant sometime in the future: i.e. a tenant can be either enabled or disabled. I need to add this data (_is_enabled_) to my data schema in order to implement this new requirement. My first inclination is to add it to the tenant
table since thats what the data is associated with. I now need to implement authz to allow only certain users to access tenants that are disabled. If I am thinking about this right, I would need to add an additional cerbos policy check for the resource tenant
where I could specify that only a user with a certain role or whatever can access tenants that are disabled. I'd then need to combine the filters from these two cerbos query plan checks into the query I ultimately send to my database.
The case above ^ seems to me like I smearing my authz logic around across my database, my app and then cerbos. If a third requirement came in that required another cerbos policy to be created, I'm not sure what advantages it'd be providing me at that point. The crux of my confusion is stemming from specifying authorization policies for resources that have relationships, particularly one-to-many and many-to-many.Alex Olivier (Cerbos)
09/15/2022, 7:26 PMWhat would you change about your cerbos policy for aif acontact
didn’t have a singularcontact
but instead had a list of user ids that had ownership (or access) to it?ownerId
---
apiVersion: "api.cerbos.dev/v1"
description: |-
Common dynamic roles used within the app
derivedRoles:
name: common_roles
definitions:
- name: owner
parentRoles: ["user"]
condition:
match:
expr: >
request.principal.id in request.resource.attr.ownerIds
If I am thinking about this right, I would need to add an additional cerbos policy check for the resourceOn this point, Cerbos does as much upfront evaluation as it can based on the context that is in the request. If the policy were to look something like this: https://play.cerbos.dev/p/hh9bOeOwW63238213E0Bs90WCOFWf6R9where I could specify that only a user with a certain role or whatever can access tenants that are disabled. I’d then need to combine the filters from these two cerbos query plan checks into the query I ultimately send to my database.tenant
apiVersion: api.cerbos.dev/v1
resourcePolicy:
version: default
resource: tenant
rules:
- actions:
- "view"
effect: EFFECT_ALLOW
roles:
- ADMIN
- actions:
- "view"
effect: EFFECT_ALLOW
roles:
- USER
condition:
match:
all:
of:
- expr: request.resource.attr.enabled == true
- expr: >
request.principal.id in request.resource.attr.ownerIds
If cerbos were to get a request with the principal having the role of ADMIN, then it will return a query plan as always allow response - thus not requiring any additional conditions in your query
If cerbos were to get a request with the principal having the role of USER then it would return the query plan with conditions for checking that the enabled attribute of the tenant is true and the requests principal id is in the list of the owners for the tenantID like so:
{
"requestId": "query-plan",
"action": "view",
"resourceKind": "tenant",
"filter": {
"kind": "KIND_CONDITIONAL",
"condition": {
"expression": {
"operator": "and",
"operands": [
{
"expression": {
"operator": "eq",
"operands": [
{
"variable": "request.resource.attr.enabled"
},
{
"value": true
}
]
}
},
{
"expression": {
"operator": "in",
"operands": [
{
"value": "user1"
},
{
"variable": "request.resource.attr.ownerIds"
}
]
}
}
]
}
}
}
}
There doesn’t have to a be a 1:1 mapping between the Cerbos attributes and the DB schema - and infact some implementations have put a layer between the app DB and Cerbos to handle the aggregation of the metadata across different microservices, it all comes down to how your system is designed.
The prisma adapter not supporting some is an oversight, do you have an idea of how you would like to see it implemented? One way this could be done today is letting the adapater map the ‘nested’ fields into a some temporary value, and then before passing it into the prisma where call, remap it how you need. not ideal, but i think will work until we can come up with a neater design without the adapter needing to be aware of the full schemaNabil
09/15/2022, 10:26 PMtenant_users: {
some: {
user_id: user.sub
}
}
So maybe if in the fieldNameRemapper
we could specify the nesting structure? Something like this:
const filters = queryPlanToPrisma({
queryPlan: tenantsQueryPlan,
fieldNameMapper: {
'request.resource.attr.user_id': 'tenant_users:some:user_id' // Some means of specifying the nesting
}
});
some
is one of three available filters for nested -to-many queries. There is `some`, `every`, and `none`.Alex Olivier (Cerbos)
09/20/2022, 10:45 AMNabil
09/20/2022, 3:55 PMAlex Olivier (Cerbos)
09/20/2022, 4:08 PM