<@U04UQ68083H> When to use SubjectSet as a type in...
# ory-copilot
c
@chilly-king-10285 When to use SubjectSet as a type in Ory Permissions Language
b
A message from kapa.ai
In the Ory Permission Language, you should use
SubjectSet
as a type when you need to define relationships that involve indirect subjects or implement hierarchical access patterns. A
SubjectSet
refers to all subjects who have the same relation on an object. This is particularly useful in the following scenarios: 1. Implementing role-based access control (RBAC): When you want to grant permissions to users based on their roles or group memberships. 2. Creating hierarchical permissions: When you need to implement inheritance of permissions through a hierarchy. 3. Defining indirect relationships: When subjects are related to objects through other subjects. Here's an example from the Ory Permission Language documentation:
Copy code
typescript class User implements Namespace {   related: { manager: User[] } }  class Group implements Namespace {   related: {     // Members can be Users or other Groups     members: (User | Group)[]   } }  class Folder implements Namespace {   related: {     parents: Folder[]     // SubjectSet<Group, "members"> refers to all subjects that are members of a Group     viewers: SubjectSet<Group, "members">[]   }    permits = {     view: (ctx: Context): boolean =>       this.related.viewers.includes(ctx.subject) ||       this.related.parents.traverse((p) => p.permits.view(ctx)),   } }
In this example,
SubjectSet<Group, "members">
is used to indicate that all members of a Group should have the "viewers" relation to a Folder. This means that if a Group has the "viewers" relation to a Folder, all members of that Group will inherit that relation. The syntax for
SubjectSet
is
SubjectSet<T, "r">
where: -
T
is the namespace (class) that contains the relation -
"r"
is the name of the relation in that namespace This powerful feature allows you to create complex permission models with minimal explicit relationship definitions, as it leverages the graph of relations to determine permissions [Graph of relations](https://www.ory.sh/docs/keto/concepts/graph-of-relations).
c
@chilly-king-10285 Should SubjectSet be used in this config: import { Namespace, Context } from “@ory/keto-namespace-types” class User implements Namespace { } class Organization implements Namespace { related: { owners: User[] admins: User[] } permits = { create: (ctx: Context): boolean => this.related.owners.includes(ctx.subject) || this.related.admins.includes(ctx.subject), create_organization_admin: (ctx: Context): boolean => this.related.owners.includes(ctx.subject), create_publication_admin: (ctx: Context): boolean => this.related.owners.includes(ctx.subject) || this.related.admins.includes(ctx.subject), read: (ctx: Context): boolean => this.related.owners.includes(ctx.subject) || this.related.admins.includes(ctx.subject), update: (ctx: Context): boolean => this.related.owners.includes(ctx.subject) || this.related.admins.includes(ctx.subject), delete: (ctx: Context): boolean => this.related.owners.includes(ctx.subject) || this.related.admins.includes(ctx.subject), } } class Publication implements Namespace { related: { organizations: Organization[] admins: User[] editors: User[] } permits = { create: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.editors.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.create(ctx)), create_publication_admin: (ctx: Context): boolean => this.related.organizations.traverse((org) => org.permits.create_publication_admin(ctx)), create_publication_editor: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.create(ctx)), read: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.editors.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.read(ctx)), update: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.editors.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.update(ctx)), delete: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.editors.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.delete(ctx)), } } class Article implements Namespace { related: { publications: Publication[] editors: User[] collaborators: User[] } permits = { create: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.create(ctx)), create_article_editor: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.create(ctx)), create_article_collaborator: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.create(ctx)), read: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.read(ctx)), read_author_id: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.read(ctx)), update: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.update(ctx)), delete: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.delete(ctx)), } }
b
A message from kapa.ai
Based on your permission model, you could benefit from using
SubjectSet
in certain places to create more efficient indirect relationships. Let me explain where and why: ## Where to Use SubjectSet In your current configuration, you're using the
traverse
method to check permissions across hierarchical relationships (from Article to Publication to Organization). While this works, using
SubjectSet
could simplify some relationships and make your permission model more efficient in specific scenarios. For example, in the
Publication
namespace, you could use
SubjectSet
to directly reference Organization admins and owners:
Copy code
typescript class Publication implements Namespace {   related: {     organizations: Organization[]     admins: User[]     editors: User[]     // Add these SubjectSets to directly reference organization roles     organizationAdmins: SubjectSet<Organization, "admins">[]     organizationOwners: SubjectSet<Organization, "owners">[]   }      permits = {     // Then you could simplify some permission checks     create: (ctx: Context): boolean =>       this.related.admins.includes(ctx.subject) ||       this.related.editors.includes(ctx.subject) ||       this.related.organizationAdmins.includes(ctx.subject) ||       this.related.organizationOwners.includes(ctx.subject),     // Other permissions similarly simplified   } }
## Benefits of Using SubjectSet Using
SubjectSet
in this way would provide several advantages: 1. Performance: Direct
includes
checks are generally more efficient than traversing relationships and checking permissions recursively [Graph of relations](https://www.ory.sh/docs/keto/concepts/graph-of-relations). 2. Clarity: The permission model becomes more explicit about which roles from parent objects have which permissions. 3. Flexibility: It allows you to selectively grant permissions to specific roles from parent objects rather than inheriting all permissions. ## When to Keep Your Current Approach Your current approach using
traverse
is appropriate when: 1. You want to maintain a strict hierarchical permission model where changes to parent permissions automatically propagate. 2. The permission logic at each level is complex and can't be easily represented by simple role membership. 3. You need to check different permissions at different levels of the hierarchy. The current model is valid and follows good practices for hierarchical permissions in Ory Permission Language. Whether to introduce
SubjectSet
depends on your specific requirements for performance, maintainability, and the exact semantics of your permission model. If you decide to keep your current approach, it's still well-structured and follows the principles of the Ory Permission Language [What is the Ory Permission Language](https://www.ory.sh/blog/what-is-the-ory-permission-language).