Hi. I have an issue with Role Policies on Cerbos 0...
# help
b
Hi. I have an issue with Role Policies on Cerbos 0.42.0 and need some clarification
I have the following
resource policy
Copy code
{
  "apiVersion": "api.cerbos.dev/v1",
  "resourcePolicy": {
    "version": "default",
    "resource": "/api/v2/tenants",
    "rules": [
      {
        "actions": [
          "get"
        ],
        "effect": "EFFECT_ALLOW",
        "name": "get.api.v2.tenants",
        "roles": [
          "admin"
        ]
      }
    ]
  }
}
If I send in the following request, I get the expected
ALLOW
Copy code
{
  "requestId": "test01",
  "resources": [
    {
      "resource": {
        "policyVersion": "default",
        "kind": "/api/v2/tenants",
        "id": "restResource"
      },
      "actions": [
        "get"
      ]
    }
  ],
  "principal": {
    "id": "<mailto:admin@domain.com|admin@domain.com>",
    "roles": [
      "admin"
    ],
    "attr": {
      "tenantID": "ABC"
    }
  },
  "includeMeta": true
}
Now I want to create a custom role which also get access to the resource with the
role policy
Copy code
{
  "apiVersion": "api.cerbos.dev/v1",
  "rolePolicy": {
    "role": "custom_role",
    "parentRoles": [
      "User"
    ],
    "rules": [
      {
        "resource": "/api/v2/tenants",
        "allowActions": [
          "get"
        ],
        "condition": {
          "match": {
            "expr": "P.attr.tenantID == \"ABC\""
          }
        }
      }
    ]
  }
}
If I send the previous request, I still get
EFFECT_ALLOW
but if I send in custom_role
Copy code
{
  "requestId": "test01",
  "resources": [
    {
      "resource": {
        "policyVersion": "default",
        "kind": "/api/v2/tenants",
        "id": "restResource"
      },
      "actions": [
        "get"
      ]
    }
  ],
  "principal": {
    "id": "admin@domain.com",
    "roles": [
      "custom_role"
    ],
    "attr": {
      "tenantID": "ABC"
    }
  },
  "includeMeta": true
}
I get
Copy code
{
  "code": 13,
  "message": "Policy check failed"
}
With a good stacktrace
Copy code
{
  "log.level": "error",
  "@timestamp": "2025-04-01T15:41:55.709Z",
  "log.logger": "cerbos.postgres",
  "message": "Query",
  "sql": "SELECT \"p0\".\"id\" AS \"unit_id\", \"p0\".\"id\", \"p0\".\"definition\" FROM \"policy\" AS \"p0\" WHERE ((\"p0\".\"id\" IN ()) AND (\"p0\".\"disabled\" = FALSE)) UNION (SELECT \"p0\".\"id\" AS \"unit_id\", \"p1\".\"id\", \"p1\".\"definition\" FROM \"policy\" AS \"p0\" INNER JOIN \"policy_dependency\" AS \"j0_1\" ON (\"p0\".\"id\" = \"j0_1\".\"policy_id\") INNER JOIN \"policy\" AS \"p1\" ON ((\"p1\".\"id\" = \"j0_1\".\"dependency_id\") AND (\"p1\".\"disabled\" = FALSE)) WHERE ((\"p0\".\"id\" IN ()) AND (\"p0\".\"disabled\" = FALSE))) UNION (SELECT \"p0\".\"id\" AS \"unit_id\", \"p2\".\"id\", \"p2\".\"definition\" FROM \"policy\" AS \"p0\" INNER JOIN \"policy_dependency\" AS \"j0_1\" ON (\"p0\".\"id\" = \"j0_1\".\"policy_id\") INNER JOIN \"policy\" AS \"p1\" ON ((\"p1\".\"id\" = \"j0_1\".\"dependency_id\") AND (\"p1\".\"disabled\" = FALSE)) INNER JOIN \"policy_dependency\" AS \"j1_2\" ON (\"p1\".\"id\" = \"j1_2\".\"policy_id\") INNER JOIN \"policy\" AS \"p2\" ON ((\"p2\".\"id\" = \"j1_2\".\"dependency_id\") AND (\"p2\".\"disabled\" = FALSE)) WHERE ((\"p0\".\"id\" IN ()) AND (\"p0\".\"disabled\" = FALSE))) UNION (SELECT \"p0\".\"id\" AS \"unit_id\", \"p1\".\"id\", \"p1\".\"definition\" FROM \"policy\" AS \"p0\" INNER JOIN \"policy_ancestor\" AS \"j0_1\" ON (\"p0\".\"id\" = \"j0_1\".\"policy_id\") INNER JOIN \"policy\" AS \"p1\" ON ((\"p1\".\"id\" = \"j0_1\".\"ancestor_id\") AND (\"p1\".\"disabled\" = FALSE)) WHERE ((\"p0\".\"id\" IN ()) AND (\"p0\".\"disabled\" = FALSE))) UNION (SELECT \"p0\".\"id\" AS \"unit_id\", \"p2\".\"id\", \"p2\".\"definition\" FROM \"policy\" AS \"p0\" INNER JOIN \"policy_ancestor\" AS \"j0_1\" ON (\"p0\".\"id\" = \"j0_1\".\"policy_id\") INNER JOIN \"policy\" AS \"p1\" ON ((\"p1\".\"id\" = \"j0_1\".\"ancestor_id\") AND (\"p1\".\"disabled\" = FALSE)) INNER JOIN \"policy_dependency\" AS \"j1_2\" ON (\"p1\".\"id\" = \"j1_2\".\"policy_id\") INNER JOIN \"policy\" AS \"p2\" ON ((\"p2\".\"id\" = \"j1_2\".\"dependency_id\") AND (\"p2\".\"disabled\" = FALSE)) WHERE ((\"p0\".\"id\" IN ()) AND (\"p0\".\"disabled\" = FALSE))) UNION (SELECT \"p0\".\"id\" AS \"unit_id\", \"p3\".\"id\", \"p3\".\"definition\" FROM \"policy\" AS \"p0\" INNER JOIN \"policy_ancestor\" AS \"j0_1\" ON (\"p0\".\"id\" = \"j0_1\".\"policy_id\") INNER JOIN \"policy\" AS \"p1\" ON ((\"p1\".\"id\" = \"j0_1\".\"ancestor_id\") AND (\"p1\".\"disabled\" = FALSE)) INNER JOIN \"policy_dependency\" AS \"j1_2\" ON (\"p1\".\"id\" = \"j1_2\".\"policy_id\") INNER JOIN \"policy\" AS \"p2\" ON ((\"p2\".\"id\" = \"j1_2\".\"dependency_id\") AND (\"p2\".\"disabled\" = FALSE)) INNER JOIN \"policy_dependency\" AS \"j2_3\" ON (\"p2\".\"id\" = \"j2_3\".\"policy_id\") INNER JOIN \"policy\" AS \"p3\" ON ((\"p3\".\"id\" = \"j2_3\".\"dependency_id\") AND (\"p3\".\"disabled\" = FALSE)) WHERE ((\"p0\".\"id\" IN ()) AND (\"p0\".\"disabled\" = FALSE)))",
  "args": [
    {
      "16": 1,
      "17": 1,
      "20": 1,
      "21": 1,
      "23": 1,
      "26": 1,
      "28": 1,
      "29": 1,
      "700": 1,
      "701": 1,
      "1082": 1,
      "1114": 1,
      "1184": 1
    }
  ],
  "err": "ERROR: syntax error at or near \")\" (SQLSTATE 42601)",
  "time": 494040,
  "pid": 1502817
}
This looks like a bug to me. In any case, I expected the role policy to grant access for someone with role custom_role and
P.attr.tenantID=ABC
despite the resource policy restricting access to admin. Is that expectation correct?
Also tried with
Copy code
/ # ./cerbos --version
0.41.0
Build timestamp: 2025-03-05T09:59:32Z
Build commit: 5d0095e41f6f1ec07d62b54d7696acb2f2706d15
Go version: go1.24.0
vcs: git
vcs.revision: 5d0095e41f6f1ec07d62b54d7696acb2f2706d15
vcs.time: 2025-03-05T09:53:02Z
vcs.modified: false
Same issue. We are using postgres as a storage backend
s
> I expected the role policy to grant access for someone with role custom_role and
P.attr.tenantID=ABC
despite the resource policy restricting access to admin. Is that expectation correct? No, the role policy would never issue an ALLOW as it can only "narrow" the permissions of it's
parentRole
. Put another way, the role policy can only affect a subset of what the
parentRole
already permits. In your example, the resource policy specifies a permission for a role
admin
, but the role policy specifies a parentRole
User
. If the
User
doesn't exist in the system with (at least) the same permissions as are granted in the role policy, it will always DENY. The other stack trace is likely an unrelated issue (might be misconfiguration), I‘ve raised an issue so we can check to see if there’s something awry!
I've easily recreated the second issue, looking at it now, thanks for raising 👍
gratitude thank you 1