Skip to content

docs: rewrite OPL as practical guide#2588

Open
DavudSafarli wants to merge 3 commits into
masterfrom
opl-update
Open

docs: rewrite OPL as practical guide#2588
DavudSafarli wants to merge 3 commits into
masterfrom
opl-update

Conversation

@DavudSafarli
Copy link
Copy Markdown
Contributor

This PR rewrites OPL into a practical how-to guide covering namespaces, relations, subject-set references, and permits, each section with examples

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR rewrites the Ory Permission Language (OPL) reference page from a formal specification into a practical how-to guide, introducing core modeling concepts (namespaces, relations, subject-set references, and permits) via short examples.

Changes:

  • Replaced the EBNF/spec-style content with a step-by-step, example-driven guide.
  • Added focused examples for unions, SubjectSet<...> references, includes, traverse, boolean operators, and permission composition.
  • Consolidated into a single “complete example” schema showing direct, group-based, and inherited access patterns.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/keto/reference/ory-permission-language.mdx
Comment thread docs/keto/reference/ory-permission-language.mdx Outdated
Comment on lines +127 to +130
restricted: (ctx: Context) =>
this.related.allowlist.includes(ctx.subject) &&
!this.related.blocklist.includes(ctx.subject),
```
Comment on lines +137 to +138
edit: (ctx: Context) => this.related.owners.includes(ctx.subject),
admin: (ctx: Context) => this.permits.edit(ctx) && this.related.admins.includes(ctx.subject),
Comment thread docs/keto/reference/ory-permission-language.mdx Outdated
Comment thread docs/keto/reference/ory-permission-language.mdx Outdated
Comment thread docs/keto/reference/ory-permission-language.mdx
ClassDecl = "class" identifier "implements" "Namespace" "{" ClassSpec "}" .
ClassSpec = [ RelationDecls ] | [ PermissionDefns] .
```
### Subject-set references
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to completely omit this section, as subject sets are not really useful currently. There is no specific reason to use them, right?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. Based on how we implement things, currently subjectsets are faster than traverse calls. In a place that traverse would cause 100 direct check calls, SubjectSet<> would make 1 call and get the answer.

But that's an implementation details, and technically, we can make optimizations to make them equally performant.

I think that's the only difference. Otherwise, Traverse is more future-proof as we can easily update/change the relationships form .members.includes(.) to .admin.includes(.).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, see this answer: https://github.com/orgs/authzed/discussions/2851#discussioncomment-15608216

This is also one difference, though i don't think anyone would care about it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, but the use-case is very narrow.
IMO the only relevant use-case is where the relation is dynamic, so the user can choose whether they want to grant permissions to all group members, only group admins, ... That requires the relation to not be in the OPL though, or allow narrowing it down to multiple relations. The use-case is not supported really rn.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The final example makes use SubjectSets, and while changing it to avoid using SubjectSets, i had difficulty. I think there's still some value in mentioning this. Also, people can be migrating from spicedb or something else to keto, and they have a working schema in their mind that uses SubjectSet. I think it's worth mentioning this.

```

Note that all relations are defined as array types `T[]` because there are naturally only many-to-many relations in Keto.
A subject can now be either a `User` directly, or any member of the `Group:engineering`.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear to me what this means.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i improved the wording as:

This means a viewer can be either a `User` directly, or any subject in the `members` relation of a `Group`. You can then write a tuple that grants access to a whole group at once:

Comment thread docs/keto/reference/ory-permission-language.mdx Outdated
Comment thread docs/keto/reference/ory-permission-language.mdx Outdated
Comment thread docs/keto/reference/ory-permission-language.mdx Outdated
Comment on lines 149 to 151
related: {
members: (User | Group)[]
members: (User | SubjectSet<Group, "members">)[]
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the subject set works, I don't think it is the correct way of doing this. Having the parents explicitly here makes it a lot clearer and allows the relation to be reused in different permissions if one wants to add them (think admins of groups, ...)

Suggested change
related: {
members: (User | Group)[]
members: (User | SubjectSet<Group, "members">)[]
}
related: {
members: User[]
parents: Group[]
}
permits = {
isMember: (ctx: Context) => this.related.members.includes(ctx.subject) || this.related.parents.traverse((parent) => parent.permits.isMember(ctx))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I admit that it is not so nice in the other namespaces without the planned type assertions, but I think it is the better way of writing permissions.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving to separate User and Group relations, this cascades to other namespaces as well; now we can't do:

class Folder implements Namespace {
  related: {
    viewers: (User | SubjectSet<Group, "members">)[]
  }

instead we have to do:

userViewers: User[]
groupViewers: Group[]

and have a permit that does the OR logic. same goes for File, but more complex to read, bc it has viewers and owners relations, so it will become viewerUsers, viewerGroups, ownerUsers, ownerGroups.

We can remove the inheritance logic, and have simpler example to avoid this.

I guess this is "simplicity" that SubjectSets brings.

Comment on lines +179 to +183
This schema models:

- Direct access via `viewers` and `owners`
- Group-based access via `SubjectSet<Group, "members">`
- Inherited access from parent folders via `traverse`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding some of these details as comments, so it is more clear what you refer to. Consider adding more comments to the full example in general.

@DavudSafarli
Copy link
Copy Markdown
Contributor Author

@zepatrik i replied to your comments. There are few things to resolve about subjectsets and final example.

It's worth noting that reader ideally should read the other pages such as Namespaces/Objects/Subjects before jumping into OPL page. So, some understanding of the Namespace/Object/Subject should exist while reading this. This page is only for OPL reference. Not 1 page Keto guide.

Though, the other pages also need improvement, and overall sidebar needs improvements. As a reader, i alway preferred 1 long page docs compared to small separate pages.
Also, if we have more "how to model rbac/gdrive/notion" guides, then there are more changes for users/llms to understand the OPL to build the right model.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants