Hey <@U03CD66UB0E> - Happy Friday. How can we help...
# community
a
Hey @Gavin Ray - Happy Friday. How can we help you with Cerbos?
g
Heya, saw Cerbos mentioned on HackerNews recently. Watched the Prisma demo you did. It looked really interesting I had a question about the Query Planner API, if you don't mind
a
Sure
g
I'm trying to understand how a scenario like the below might work: • You have tabular data, say a CSV file of
user
rows • A REST API is generated on top of the tabular data, to read the rows • I make a Cerbos policy, saying that the
id
column of a row must match the
user_id
claim of a JWT I've signed Would the Query Planner give me a way to programmatically push this predicate into my CSV filtering logic, or say a SQL string that was generated -- or how does it work? 🤔
I'm imagining that hitting
GET /users
would just return
SELECT * FROM csv_users
but I'd like to process the Cerbos policy and push the
WHERE csv_users.id == user_id
into the operation
a
Yup that is exactly how it works. You would have a Cerbos policy for a
User
resource that says the
read
action is allowed with a condition of the
request.resource.id == request.principal.id
(or you can send the whole JWT)
Copy code
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: default
  resource: user_resource
  rules:
    - actions:
        - "read"
      effect: EFFECT_ALLOW
      roles:
        - USER
      condition:
        # ensure the ID field of the user is requal to the ID of the user making the request
        match:
          expr: request.resource.id == request.principal.id
Here is an example https://play.cerbos.dev/p/nDT6Y0KVilOQd6262f1485AffybQAqYd Whilst not support in the playground just yet, the Query Plan request would look like this:
Copy code
{
  "requestId": "123123",
  "action": "read",
  "principal": {
    "id": "123",
    "roles": [
      "USER"
    ],
    "attr": {}
  },
  "resource": {
    "kind": "user_resource"
  }
}
and the response
Copy code
{
  "requestId": "123123",
  "action": "read",
  "resourceKind": "user_resource",
  "filter": {
    "kind": "KIND_CONDITIONAL",
    "condition": {
      "expression": {
        "operator": "eq",
        "operands": [
          {
            "variable": "request.resource.id"
          },
          {
            "value": "123"
          }
        ]
      }
    }
  }
}
This response is a standard format which you can use to apply a ‘filter’ to the data returned. Here is a cURL to the playground PDP instance if you want to try it:
Copy code
curl --location --request POST '<https://demo-pdp.cerbos.cloud/api/x/plan/resources>' \
--header 'Playground-Instance: nDT6Y0KVilOQd6262f1485AffybQAqYd' \
--header 'Content-Type: application/json' \
--data-raw '{
    "requestId": "123123",
    "action": "read",
    "principal": {
        "id": "123",
        "roles": [
            "USER"
        ],
        "attr": {}
    },
    "resource": {
        "kind": "user_resource"
    }
}'
g
Oh wow -- thank you! And then the business logic would take the response, and visit the
expression
AST to translate the filters into whatever domain-specific functionality you need?
Copy code
function convertCerbosFilterToSQL(filterExpr, requestObject) {
And you'd just hand this function the
response
from the Query Planner endpoint and the stateful request resource object
a
Correct!
We’ve designed it that way to be agnostic to the data storage and in larger apps you maybe fetching data from a DB, cache, a NoSQL DB etc so we didn’t want to couple the implementation to any particular data model.
💯 1