Hey! Our team is struggling a bit to properly mode...
# talk-keto
v
Hey! Our team is struggling a bit to properly model a recursive hierarchical permission system in Keto. The main requirements are: 1.
Organizations
contain
members
, a
member
can also be an
owner
2. A
Organization
can be a child of another
Organization
3.
owners
and
members
of a parent
Organization
are also
owners
and
members
of any child
Organization
4.
owners
can
manage
an
Organization
, including any child
Organizations
A simplified proof of concept using OPL
Copy code
class Organization implements Namespace {
  related: {
    parent: Organization[]
    owners: User[]
    members: User[]
  }
  permits = {
    manage: (ctx: Context) => this.related.owners.includes(ctx.subject) || this.related.parent.traverse(o => o.permits.manage(ctx)),
  }
}
We're having issues with the hierarchical permission checks, consider the following scenario:
Copy code
Organization:1
    owners: [User:1]
    members: [User:1, User:2]
Organization:1.1
    parent: [Organization:1]
    members: [User:3]
When I check
if User:1 can manage Organization1.1
, I would expected yes but we're getting a no
s
I cannot spot any obvious error in the OPL, how are you sending those requests? So both create & check requests
f
I had a similar issue in my testing. Discussed with @magnificent-energy-493 in our private thread a bit as well. I don’t know if it counts as a Keto bug or not, but what I tried first was:
Copy code
import { Namespace, Context } from "@ory/keto-namespace-types"

class User implements Namespace {}
class Group implements Namespace {
    related: {
        members: User[]
    }

    permits = {
        view_members: (ctx: Context): boolean =>
            this.related.members.includes(ctx.subject),
    }
}
class Document implements Namespace {
    related: {
        groups: Group[]
    }

    permits = {
        read: (ctx: Context): boolean =>
            this.related.groups.traverse((group) => group.related.members.includes(ctx.subject)),
    }
}
This is like yours, but didn’t work for me.
read
is always denied. I was surprised. I refactored to this:
Copy code
import { Namespace, SubjectSet, Context } from "@ory/keto-namespace-types"

class User implements Namespace {}
class Group implements Namespace {
    related: {
        members: User[]
    }

    permits = {
        view_members: (ctx: Context): boolean =>
            this.related.members.includes(ctx.subject),
    }
}
class Document implements Namespace {
    related: {
        collaborators: (User | SubjectSet<Group, "members">)[]
    }

    permits = {
        read: (ctx: Context): boolean =>
            this.related.collaborators.includes(ctx.subject),
    }
}
This worked. Tuples are like this:
Copy code
{
  "namespace": "Document",
  "object": "bake-a-cake-recipe",
  "relation": "collaborators",
  "subject_set": {
    "namespace": "Group",
    "object": "red-group",
    "relation": "members"
  }
}
v
Thank you both, I had a hunch the solution was related to
SubjectSets
, I still find a bit hard to understand how they work, couldn't find much in the docs about subject sets