<@U04UQ68083H> Show an example of using the expand...
# ory-copilot
c
@chilly-king-10285 Show an example of using the expand API to get a list of all subject IDs that have access to an object.
b
A message from kapa.ai
Certainly! Here’s an example of using the Ory Keto expand API to get a list of all subject IDs (users) that have access to a specific object. Suppose you have a file sharing application, and you want to know who has access to the file
/photos/beach.jpg
in the
files
namespace, specifically for the
access
relation. ### Using the Keto CLI You can use the following command:
Copy code
keto expand access files /photos/beach.jpg
This will output a tree showing all subjects (users or groups) who have access, either directly or indirectly, to the file. For example, the output might look like:
Copy code
∪ files:/photos/beach.jpg#access ├─ ∪ files:/photos/beach.jpg#owner │  ├─ ☘ maureen ├─ ∪ directories:/photos#access │  ├─ ☘ laura
This means: -
maureen
has access because she is the owner. -
laura
has access because she was granted access to the parent directory. ### Using the REST API You can also use the REST API:
Copy code
GET /relation-tuples/expand?namespace=files&object=/photos/beach.jpg&relation=access
This will return a JSON tree structure with all the subjects who have access, including the reason (direct or via group/parent). ### Notes - The expand API returns a tree, not just a flat list, so you may need to traverse the tree to extract all subject IDs. - You can limit the depth of the expansion using the
max-depth
parameter to control performance and output size. For more details and examples, see the official documentation: [Expand API: display who has access to an object](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access#example) and [Keto CLI expand command](https://www.ory.sh/docs/keto/cli/keto-expand#keto-expand).
c
Imagine I have the following namespaces: Organizations, Publications, Articles. Organization owners and admins have read, update, and delete permits on all objects within their organization. Publication editors and admins have read, update, and delete permits on all objects within their publication. Article editors, admins, and collaborators have read, update, delete permits on specific articles. Show an example of using the expand api to get the subject ID of all users that have update access to an object called art_123.
@chilly-king-10285 Imagine I have the following namespaces: Organizations, Publications, Articles. Organization owners and admins have read, update, and delete permits on all objects within their organization. Publication editors and admins have read, update, and delete permits on all objects within their publication. Article editors, admins, and collaborators have read, update, delete permits on specific articles. Show an example of using the expand api to get the subject ID of all users that have update access to an object called art_123.
b
A message from kapa.ai
Certainly! Here’s how you would use the Ory Keto expand API to get all subject IDs (users) that have
update
access to an article object called
art_123
, based on your described permission model. ### 1. Using the Expand API You would call the expand API for the
update
relation on the
Articles
namespace and the object
art_123
. For example, using the Keto CLI:
Copy code
keto expand update Articles art_123
Or, using the REST API:
Copy code
GET /relation-tuples/expand?namespace=Articles&object=art_123&relation=update
### 2. What the Expansion Tree Shows The expand API will return a tree structure that includes all users (subject IDs) who have
update
access to
art_123
, either directly (e.g., as article editors, admins, collaborators) or indirectly (e.g., via publication or organization roles, if your permission model is set up accordingly). The output will look similar to this (the actual structure depends on your namespace and relation configuration):
Copy code
∪ Articles:art_123#update ├─ ∪ Articles:art_123#admin │  ├─ ☘ user_alice ├─ ∪ Articles:art_123#editor │  ├─ ☘ user_bob ├─ ∪ Articles:art_123#collaborator │  ├─ ☘ user_charlie ├─ ∪ Publications:pub_456#admin │  ├─ ☘ user_dana ├─ ∪ Publications:pub_456#editor │  ├─ ☘ user_erin ├─ ∪ Organizations:org_789#admin │  ├─ ☘ user_frank ├─ ∪ Organizations:org_789#owner │  ├─ ☘ user_grace
Each ☘️ leaf node is a subject ID (user) who has
update
access, either directly or through a higher-level role. ### 3. Notes - The actual users and structure depend on your relationship tuples and namespace configuration. - You may need to traverse the returned tree to extract all subject IDs programmatically. - The expand API is designed to show not just who has access, but also why (through which relation or inheritance) [Expand API: display who has access to an object](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access#example). - You can limit the depth of the expansion with the
max-depth
parameter if needed [Maximum tree depth](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access#maximum-tree-depth). For more details, see the [Keto CLI expand command documentation](https://www.ory.sh/docs/keto/cli/keto-expand#keto-expand) and the [Expand API guide](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access#example).
c
@chilly-king-10285 The OPL looks like this: 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
Given your OPL (Ory Permission Language) model, to get all subject IDs (users) that have
update
access to an article object called
art_123
, you would use the expand API on the
update
relation for the
Article
namespace and the object
art_123
. ### How to Use the Expand API #### Using the Keto CLI
Copy code
sh keto expand update Article art_123
#### Using the REST API
Copy code
GET /relation-tuples/expand?namespace=Article&object=art_123&relation=update
### What the Expand API Returns The expand API will return a tree structure that includes all users who have
update
access to
art_123
, either directly (as editors or collaborators) or indirectly (via publication or organization roles, as defined in your OPL). You will need to traverse the tree to extract all subject IDs. The tree will reflect the logic in your OPL, which for
Article.update
is: - Direct:
editors
and
collaborators
of the article. - Indirect: Any user who has
update
access via the related
Publication
(which itself may include users with
admin
or
editor
roles, or those with access via the related
Organization
). The expand API is specifically designed to show all effective subjects and the reason for their access, matching your use case perfectly. You may use the
max-depth
parameter to control how deep the tree is expanded if you want to limit indirect relationships for performance or clarity reasons [Expand API: display who has access to an object](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access#example) [Maximum tree depth](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access#maximum-tree-depth). Note: The actual list of subject IDs will depend on your stored relation tuples and the relationships between articles, publications, organizations, and users in your system. For more details, see the [Expand API documentation](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access#example).
c
@chilly-king-10285 THis doesn’t work:
GET /relation-tuples/expand?namespace=Articomes cle&object=art_123&relation=update
it replies with
{"code":404,"status":"Not Found","message":"no relation tuple found"}
. It only “works” when I format the request to check a direct relation (like “editors”, not a permit associated with a relation like “update”.
b
A message from kapa.ai
Thank you for your detailed feedback and for pointing out the issue. Based on the knowledge sources, the expand API in Ory Keto is designed to expand a **subject set**—that is, a direct relation tuple such as
editors
,
admins
, or similar. The API expects a relation that is directly stored as a relation tuple, not a computed permission (permit) like
update
that is defined in OPL and may be composed of multiple underlying relations or traversals. This is confirmed in the documentation, which states: > The expand-API allows to expand a given subject set into all its effective subjects. > — [Expand API: display who has access to an object](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access) And the API reference shows the parameters as
namespace
,
object
, and `relation`—where
relation
must be a direct relation tuple, not a computed permission: > Use this endpoint to expand a relationship tuple into permissions. > — [Keto REST API Reference: expandPermissions](https://www.ory.sh/docs/keto/reference/rest-api#expandpermissions) If you try to expand a computed permission (like
update
), and there is no direct relation tuple stored for that permission, the API will return a 404 Not Found, as you observed. In summary: The expand API only works for direct relations (e.g.,
editors
,
admins
) that are stored as relation tuples. It does not expand computed permissions (permits) like
update
that are defined in OPL and may aggregate multiple relations. This is a current limitation of the expand API as described in the official documentation. If you need to see all users with a computed permission like
update
, you would need to manually expand all the underlying relations (e.g.,
editors
,
collaborators
, and traverse up to publication and organization roles) using multiple expand or list queries, or by evaluating the OPL logic in your application code. If you need further details, please refer to the [Expand API guide](https://www.ory.sh/docs/keto/guides/expand-api-display-who-has-access) and the [REST API reference](https://www.ory.sh/docs/keto/reference/rest-api#expandpermissions).