Hi Cerbos team, I have several questions Let's sta...
# help
a
Hi Cerbos team, I have several questions Let's start with my requirement, i do want to make a file storage system that the user who upload files can expose file(read, write) public or private also share to specific organize, team, person so, I made a simple request and policy like this in reply (I think it's too long)
policy
Copy code
---
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: "default"
  importDerivedRoles:
    - common_roles
  resource: "album:object"
  rules:
    - actions: ['*']
      effect: EFFECT_ALLOW
      derivedRoles:
        - owner

    - actions: ['read:public']
      effect: EFFECT_ALLOW
      roles:
        - user
        - user2
      condition:
        match:
          expr: request.resource.attr.public == true

    - actions: ['read:private']
      effect: EFFECT_ALLOW
      derivedRoles:
        - anyone
      condition:
        match:
          expr: (request.resource.attr.share_list.exists(share, share.id == request.principal.id && share.read == true)) || (request.resource.attr.share_read_list.exists(share, share.id == request.principal.id)) 

    - actions: ['write:private']
      effect: EFFECT_ALLOW
      derivedRoles:
        - anyone
      condition:
        match:
          expr: request.resource.attr.share_list.exists(share, share.id == request.principal.id && share.write == true) || (request.resource.attr.share_write_list.exists(share, share.id == request.principal.id))
request
Copy code
const kind = "album:object";
const actions = ["read:public", "read:private", "write:private"];

{
    principal: {
      id: "alex",
      roles: ["user", "user2"],
      attributes: {
        beta_tester: true,
        team: "UTM",
      },
    },
    resources: [
      {
        resource: {
          kind: kind,
          id: "video_file_port.mp4",
          attributes: {
            owner: "warradon",
            public: false,
            team: "UTM",
            share_list: [
              {
                "id": "alex",
                "read": true,
                "write": true
              },
              {
                "id": "bob",
                "read": true,
                "write": false
              },
            ],
          },
        },
        actions: actions,
      },
      {
        resource: {
          kind: kind,
          id: "file2",
          attributes: {
            owner: "george",
            public: true,
            team: "UTM",
            share_read_list: [
              {
                "id": "alex",
              },
              {
                "id": "bob",
              }
            ],
            share_write_list: [
              {
                "id": "alex",
              },
            ]
          },
        },
        actions: actions,
      },
    ],
  }
according to these code, the principle alex can access both read and write in both file, but bob cannot every thing go right
1 but I do have a concern that this policy require my backend query a record of share list to build a request payload.
like this
Copy code
share_list: [
              {
                "id": "alex",
                "read": true,
                "write": true
              },
              {
                "id": "bob",
                "read": true,
                "write": false
              },
            ]
that I think I would rather not query a records from db,
I would rather make a regex condition like principle.id == "alex", but it doesn't seem correct
2. another concern is a number of policy, let's say if I have hundred thousands of files means i do have one dread thousands of files too, do you have any suggestion about this do i really need to have 1000000 of policies? or what is the number of limitation?
that's all my concern
c
Hey, it looks like you're trying to implement an ACL system where each individual instance of a resource has its own access rules. In such cases, the best thing to do is to implement the ACL membership check in your own code because that's far more efficient to do using a single database query. So, for every resource instance a user is trying to access, you should first do a quick query to see if the user is in the list of allowed users for that instance and, if so, what permissions (read, write etc.) are granted to them. Now, at this point you have two options. If your permissions are very basic, i.e
read
just means read and has no other conditions, then you probably don't even need Cerbos policies. However, if you have other requirements like "user could only read a file if they are logged in from this IP range" or "files with top secret classification requires a 2FA token" etc., then you could delegate those checks to Cerbos. Basically, you can send a principal attribute like
permissions: ["read", "write", "delete"]
based on the ACL check and then have derived roles such as
reader
,
writer
,
admin
that get activated from those values.
a
more clarify: my application was designed to have a perception from files to user, example this document file allows alex and bob to read but allows only alex to write, adding permission scope in principle is not my option
more example I as a teacher have 10 files in my workspace I share a file to each of my students, means each of my student only see his document
according to code above I can make a request to cerbos but also need to query from table that which users, teams, orgs this file share to, that I world rather not to be
I prefer make a regex condition principle.id == "alex" but this exactly condition doesn't seem correct (alex get access deny if I write this condition)
c
I don't see how you can write a regex unless the principal IDs are standardized in some way. You'd just end up enumerating all the IDs like
alex|bob|charlie
. I don't think you can avoid a database lookup for this use case either. Cerbos is stateless so you have to provide the data to it somehow. That has to come from some kind of a data store.
a
more clarify, if I add more people to share such as allow charlie to read file, I do have a record that tell which users, teams, orgs, this file share to and if any mutating operation that relate to this file, my application will query this to generate a new policy. made enum in condition is what I prefer but regex condition principle.id == "alex|bob|charlie" is not working as I expected I'm not really familiar with regex so I don't know what I missed
database will be query only to generate policy not when user want to access a file
c
Oh! You're generating a policy for each file you want to secure? That's interesting. I personally wouldn't do it that way because managing all of those policies is going to be quite painful. It'd be much more simpler to do the ACL check in the DB and use a single policy to enforce the rules. But, it's up to you. The way you write regexes in Cerbos conditions is using the
matches
operator. For example:
P.id.matches("alex|bob")
. https://docs.cerbos.dev/cerbos/latest/policies/conditions.html#_strings
a
many thank that's exactly that I want form my first concern,
and about the second concern, and you've already mentioned about it, that is maintain a large of number of policy
do you have any suggestions to minimize the number of policy, and what is the limitation of cerbos, how many policy that 1 cerbos instance can manage?
c
There's no built-in limit on the number of policies. Obviously, if you have a very large number of policies they will add overhead and performance could degrade. They will also require more CPU and memory to process. As for minimising the number of policies, I have described how to achieve this with one Cerbos policy but you have to actively use the database for that.
a
thank a lot for you helping and time