Benjamin Rupp
04/01/2025, 3:48 PMBenjamin Rupp
04/01/2025, 3:48 PMresource policy
{
"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
{
"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
}
Benjamin Rupp
04/01/2025, 3:48 PMrole policy
{
"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
{
"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
{
"code": 13,
"message": "Policy check failed"
}
Benjamin Rupp
04/01/2025, 3:49 PM{
"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
}
Benjamin Rupp
04/01/2025, 3:54 PMP.attr.tenantID=ABC
despite the resource policy restricting access to admin. Is that expectation correct?Benjamin Rupp
04/01/2025, 6:59 PM/ # ./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 backendSam Lock (Cerbos)
04/02/2025, 6:44 AMP.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!Sam Lock (Cerbos)
04/02/2025, 1:38 PM