OPL: nested traverse are not allowed
zepatrik opened this issue · 2 comments
Preflight checklist
- I could not find a solution in the existing issues, docs, nor discussions.
- I agree to follow this project's Code of Conduct.
- I have read and am following this repository's Contribution Guidelines.
- This issue affects my Ory Network project.
- I have joined the Ory Community Slack.
- I am signed up to the Ory Security Patch Newsletter.
Describe the bug
It is not allowed to have nested traverse expressions.
Reproducing the bug
See the permits section of the
S3ResourceType
, thecan_create
permission has two nested traverse calls.
class Role implements Namespace {
related: {
principal: Project[]
}
permits = {
can_assume: (ctx: Context) => this.related.principal.traverse((p) => p.related.access.includes(ctx.subject))
}
}
class Policy implements Namespace {
related: {
attach: Role[]
}
}
class S3ResourceType implements Namespace {
related: {
create: Policy[]
edit: Policy[]
read: Policy[]
}
permits = {
can_create: (ctx: Context) => this.related.create.traverse((p) => p.related.attach.traverse((r) => r.permits.can_assume(ctx)))
}
}
This is however only a limitation on the OPL level. The same result can be achieved by using:
class Policy implements Namespace {
related: {
attach: Role[]
}
permits = {
can_create_tmp: (ctx: Context) => this.related.attach.traverse((r) => r.permits.can_assume(ctx))
}
}
class S3ResourceType implements Namespace {
related: {
create: Policy[]
edit: Policy[]
read: Policy[]
}
permits = {
can_create: (ctx: Context) => this.related.create.traverse((p) => p.can_create_tmp(ctx)),
}
}
which is a lot more complex and harder to manage.
Relevant log output
No response
Relevant configuration
No response
Version
v0.10.0
On which operating system are you observing this issue?
No response
In which environment are you deploying?
No response
Additional Context
No response
I have a bit more condensed version of this bug.
OPL:
class Project implements Namespace {
related: {
companies: Company[]
}
permits = {
view: (ctx: Context) => this.related.companies.traverse(company => company.related.users.includes(ctx.subject))
}
}
class User implements Namespace {}
class Company implements Namespace {
related: {
users: User[]
}
}
So User has view permit on Project if he is in Company.
Relations:
{
"namespace": "Company",
"object": "zenmo",
"relation": "users",
"subject_id": "User:erik"
},
{
"namespace": "Project",
"object": "myproj",
"relation": "companies",
"subject_id": "Company:zenmo"
}
Permission check which returns false but should return true:
POST /relation-tuples/check/openapi
{
"namespace": "Project",
"object": "myproj",
"relation": "view",
"subject_id": "User:erik"
}
Could I ask where Keto OPL currently sits with nested traversals?
I just started using Keto again after a few months break, and spent a painful amount of time trying to get the following to work, only to find this limitation and issue via Slack 😅
import { Namespace, Context } from "@ory/keto-namespace-types"
class User implements Namespace {}
class Team implements Namespace {
related: {
owners: User[]
members: User[]
}
permits = {
delete: (ctx: Context): boolean => this.related.owners.includes(ctx.subject),
manage: (ctx: Context): boolean => this.related.owners.includes(ctx.subject),
}
}
class Project implements Namespace {
related: {
teams: Team[]
}
permits = {
manage: (ctx: Context): boolean => this.related.teams.traverse((t) => t.permits.manage(ctx)),
contribute: (ctx: Context): boolean => this.permits.manage(ctx),
}
}
class File implements Namespace {
related: {
projects: Project[]
}
permits = {
write: (ctx: Context): boolean => this.related.projects.traverse((p) => p.permits.manage(ctx)),
read: (ctx: Context): boolean => this.related.projects.traverse((p) => p.permits.contribute(ctx)),
}
}
With the following relations
[
{
"namespace": "Team",
"object": "my_team",
"relation": "owners",
"subject_id": "User:taylor"
},
{
"namespace": "Project",
"object": "my_project",
"relation": "teams",
"subject_id": "Team:my_team"
},
{
"namespace": "File",
"object": "my_file",
"relation": "projects",
"subject_id": "Project:my_project"
}
]
If I make the following requests, I get allowed: false
GET /relation-tuples/check
{
"namespace": "Project",
"object": "my_project",
"relation": "manage",
"subject_id": "User:taylor"
}
GET /relation-tuples/check
{
"namespace": "File",
"object": "my_file",
"relation": "write",
"subject_id": "User:taylor"
}
Yet, if I do the following, I get allowed: true
GET /relation-tuples/check
{
"namespace": "Team",
"object": "my_team",
"relation": "manage",
"subject_id": "User:taylor"
}
Any concrete information/status on this would be really useful, thanks!