Hello Cerbos Team, We are solving a use case with ...
# help
b
Hello Cerbos Team, We are solving a use case with PlanResources, where we need Cerbos to return the conditions that are required to access certain data.
We have resource policies defined for our endpoints Example:
Copy code
{
  "apiVersion": "api.cerbos.dev/v1",
  "resourcePolicy": {
    "version": "default",
    "resource": "/api/v1/stitchit/objects/query",
    "rules": [
      {
        "actions": [
          "post"
        ],
        "effect": "EFFECT_ALLOW",
        "name": "post.api.v1.stitchit.objects.query",
        "roles": [
          "inventory_read"
        ]
      }
    ]
  }
}
Which allows a user with
inventory_read
role to access this endpoint. These resource policies are static and should not change between releases. Now we want to restrict certain user's access to inventory objects, based on metadata provisioned on those objects. Metadata being key value pairs, such as
city=Amsterdam
For this, we use role policies, which can be changing frequently:
Copy code
"rolePolicy": {
        "role": "berlin",
        "parentRoles": [
          "inventory_read"
        ],
        "scope": "d95e67fc-64b5-4869-82bb-120b69424130.data",
        "rules": [
          {
            "resource": "*",
            "allowActions": [
              "*"
            ],
            "condition": {
              "match": {
                "all": {
                  "of": [
                    {
                      "expr": "request.resource.attr.city in [\"berlin\"]"
                    }
                  ]
                }
              }
            }
          }
        ]
      }
The role policy makes it so that my user with role
berlin
(IdP role) now has access to the endpoint
/api/v1/stitchit/objects/query
with action
post
and condition
city=berlin
Here is a request
Copy code
curl <http://localhost:3592/api/plan/resources> -d '{
 "requestId":  "test01",
 "action":  "post",
 "resource":  {
 "policyVersion": "default",
 "kind":  "/api/v1/stitchit/objects/query",
 "scope": "d95e67fc-64b5-4869-82bb-120b69424130.data"
 },
 "principal":  {
 "id":  "Ben",
 "roles":  ["berlin"]
 },
 "includeMeta": true
 }'
And the response
Copy code
{
  "requestId": "test01",
  "action": "post",
  "resourceKind": "/api/v1/stitchit/objects/query",
  "policyVersion": "default",
  "filter": {
    "kind": "KIND_CONDITIONAL",
    "condition": {
      "expression": {
        "operator": "eq",
        "operands": [
          {
            "variable": "request.resource.attr.city"
          },
          {
            "value": "berlin"
          }
        ]
      }
    }
  },
  "meta": {
    "filterDebug": "(eq request.resource.attr.city \"berlin\")"
  },
  "cerbosCallId": "01JYPBWCX841QY2ZB1H6EZQVBT"
}
👍 This is perfect and exactly what we want.
Now the issue If I add another role to my resourcePolicy: let's say contributor
Copy code
{
  "apiVersion": "api.cerbos.dev/v1",
  "resourcePolicy": {
    "version": "default",
    "resource": "/api/v1/stitchit/objects/query",
    "rules": [
      {
        "actions": [
          "post"
        ],
        "effect": "EFFECT_ALLOW",
        "name": "post.api.v1.stitchit.objects.query",
        "roles": [
          "inventory_read",
          "contributor"
        ]
      }
    ]
  }
}
And I sent in the
principal
also with this contributor role
Copy code
curl <http://localhost:3592/api/plan/resources> -d '{
 "requestId":  "test01",
 "action":  "post",
 "resource":  {
 "policyVersion": "default",
 "kind":  "/api/v1/stitchit/objects/query",
 "scope": "d95e67fc-64b5-4869-82bb-120b69424130.data"
 },
 "principal":  {
 "id":  "Ben",
 "roles":  ["berlin", "contributor"]
 },
 "includeMeta": true
 }'
Now I get:
KIND_ALWAYS_ALLOWED
It is logical to me: Because I have the contributor role, which is directly authorized by the resourcePolicy, my role policy
berlin
is not evaluated. My question is: Is there a way where I can still have the
berlin
role policy evaluated and the conditional returned? Some configuration option, or maybe a different provisioning approach? We are facing this issue, because we have to deal with legacy roles and backwards compatibility. Thank you in advance
We are using
lenientScopeSearch: true
and Cerbos 0.45.0
d
Hi Benjamin, > My question is: Is there a way where I can still have the
berlin
role policy evaluated and the conditional returned? Some configuration option, or maybe a different provisioning approach? Do you expect the output to be like
true && R.attr.city == "berlin"
or simply
R.attr.city == "berlin"
? • If the former, it’s not possible since the query planner short-circuits evaluation and further “normalises” the output. • If you meant the latter, the contributor must not be a “normal” role, but a role designated by a role policy.
b
Hey @Dennis (Cerbos), (Ben and I are working together)
• If you meant the latter, the contributor must not be a “normal” role, but a role designated by a role policy.
Yes, we are hoping for the latter. I'm not clear however what you mean by "normal" role
Basically what we're trying to do is kind of the opposite of what the Cerbos docs say here... > The query plan output was incorrectly prioritizing roles with
deny
rules when a principal had multiple roles. This could lead to overly restrictive query plans. > Consider a principal who is both an
admin
(with broad
allow
rules) and a
viewer
(with more specific
deny
rules). The
admin
role's permissions should take precedence. The query planner now correctly prioritizes the role(s) that grant access, ensuring the generated plan is not unnecessarily constrained by lower-privileged roles. In our example above, we need the roles with more restrictions to be prioritized since the "contributor" role won't have any rules set, even as its own proper role policy We really hope this is possible
d
Hi @Billy Bolton, sorry for the confusing terminology. The “contributor” role is listed in the resource policy, unlike the “berlin” role, which is created by the respective role policy. If the “contributor” was another role policy, I think you’d have the desired outcome.
b
We'll give it a shot and let you know. Thanks @Dennis (Cerbos)!
🙌 1
@Dennis (Cerbos), no luck.
Copy code
curl -X POST -k -u cerbos:cerbosAdmin '<http://localhost:3592/admin/policy>' -d '{
    "policies": [
        {
            "apiVersion": "api.cerbos.dev/v1",
            "resourcePolicy": {
                "version": "default",
                "resource": "/api/v1/stitchit/objects/query",
                "rules": [
                    {
                        "actions": [
                            "post"
                        ],
                        "effect": "EFFECT_ALLOW",
                        "name": "post.api.v1.stitchit.objects.query",
                        "roles": [
                            "inventory_read",
                            "contributor"
                        ]
                    }
                ]
            }
        },
        {
            "apiVersion": "api.cerbos.dev/v1",
            "rolePolicy": {
                "role": "berlin",
                "parentRoles": [
                    "inventory_read"
                ],
                "rules": [
                    {
                        "resource": "*",
                        "allowActions": [
                            "*"
                        ],
                        "condition": {
                            "match": {
                                "all": {
                                    "of": [
                                        {
                                            "expr": "request.resource.attr.city in [\"berlin\"]"
                                        }
                                    ]
                                }
                            }
                        }
                    }
                ]
            }
        },
        {
            "apiVersion": "api.cerbos.dev/v1",
            "rolePolicy": {
                "role": "contributor",
                "rules": [
                    {
                        "resource": "*",
                        "allowActions": [
                            "*"
                        ]
                    }
                ]
            }
        }
    ]
}
'
Copy code
curl <http://localhost:3592/api/plan/resources> -d '{
 "requestId":  "test01",
 "action":  "post",
 "resource":  {
 "policyVersion": "default",
 "kind":  "/api/v1/stitchit/objects/query"
 },
 "principal":  {
 "id":  "Ben",
 "roles":  ["berlin","contributor"]
 },
 "includeMeta": true
 }'
Copy code
{
  "requestId": "test01",
  "action": "post",
  "resourceKind": "/api/v1/stitchit/objects/query",
  "policyVersion": "default",
  "filter": {
    "kind": "KIND_ALWAYS_ALLOWED"
  },
  "meta": {
    "filterDebug": "(true)"
  },
  "cerbosCallId": "01JZ940P86W09CVTSWWKNGSGFW"
}
I recognize this behaviour is "correct and by design" but we really we need a way to prioritize more restrictive roles (ie.
berlin
). Is there maybe a rule condition we can set on the resource policy somehow that "if this role do this", sort of thing?
đź‘€ 1
d
Copy code
"resourcePolicy": {
                "version": "default",
                "resource": "/api/v1/stitchit/objects/query",
                "rules": [
                    {
                        "actions": [
                            "post"
                        ],
                        "effect": "EFFECT_ALLOW",
                        "name": "post.api.v1.stitchit.objects.query",
                        "roles": [
                            "inventory_read",
                            "contributor"
                        ]
                    }
                ]
            }
The “contributor” is listed the resourcePolicy
I’ll create an example to show what I mean.
👍 1
I haven’t had any luck with my example either. I’ll raise an issue. But first, please let me know the following. If the “berlin” role instead of
"allowActions": ["*"]
had
"allowAction: ["read"]
, what do you expect from the request with action “post” and roles [“berlin”, “contributor”]? Unconditionally allowed?
b
In your scenario, yes I'd expect (and do produce)
KIND_ALWAYS_ALLOWED
because the
berlin
role would no longer have a matching resource/*action*, and I'd expect it to fall back to the
contributor
role that does. If you do raise an issue that's externally visible, we would be grateful to follow-along to track how it might be resolved.
👍 1
If it means anything, I was just able to reproduce this issue on 0.43.0 as well, so I'm not sure the release notes for 0.44.0 & 0.45.0 are related
d
That issue, resolved in 0.45, was specific to the unconditional DENY rule.
👍 1
b
ah ok
d
My bad. It seems that the answer is no, if the “berlin” and “contributor” are unrelated roles, then one of them is enough to allow the action. In case of the “contributor”, the ALLOW is unconditional. Sorry, we’re back to square one.
🥲 1
Role policies are a relatively new topic. I’ll raise an issue if it’s possible to create a restrictive “berlin” role.
đź«¶ 1
b
Thanks Dennis. We appreciate looking into this for us
d
No worries. As a workaround, you can make separate requests for each role. In this case, you can combine results in a restrictive manner.
b
Do you know when you might get feedback on the issue you're raising? (Not trying to pressure your time, more so wondering just for our own planning). Given this issue, we're considering some painful modelling changes as a very last resort, but if you think this is a bug that could be fixed, I'm thinking your suggestion is worth implementing for the short-term. If you don't know either, no worries
d
I don’t think it’s a bug. It is a legitimate way to look at permission. It might be a missing feature. But I’ll try to find out the answer today (your tomorrow).
b
awesome, thanks again Dennis!
🙌 1
d
I got it confirmed that all roles provide a union of permissions. In your example, the intersection of permissions is required. Cerbos currently doesn’t support that, so unfortunately, re-modelling is needed.
🥲 1
b
Ok thanks Dennis! Appreciate the time you took for this
🙌 1