careful-honey-27437
04/28/2025, 2:41 AMbrief-egg-61061
04/28/2025, 2:41 AMbrief-egg-61061
04/28/2025, 2:41 AMtypescript class User implements Namespace {} class Organization implements Namespace { related: { admins: User[]; } permits = { admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) } } class Publication implements Namespace { related: { organizations: Organization[]; } permits = { admin: (ctx: Context): boolean => this.related.organizations.traverse((org) => org.permits.admin(ctx)) } } class Article implements Namespace { related: { publications: Publication[]; } permits = { read: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.admin(ctx)) } }
In this example, if user:456 is an admin of organization:789, and publication:xyz belongs to organization:789, and article:123 belongs to publication:xyz, then user:456 would have read access to article:123 through this chain of relationships. The traverse
method (or transitive
in some documentation) allows you to check permissions across related objects in different namespaces [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model). This approach is very flexible and allows you to create complex permission hierarchies that span multiple namespaces while keeping the permission logic clear and maintainable.careful-honey-27437
04/28/2025, 2:44 AMcareful-honey-27437
04/28/2025, 2:45 AMcareful-honey-27437
04/28/2025, 2:45 AMbrief-egg-61061
04/28/2025, 2:45 AMbrief-egg-61061
04/28/2025, 2:46 AMtypescript import { Namespace, Context } from "@ory/keto-namespace-types" class User implements Namespace {} class Organization implements Namespace { related: { owners: User[]; admins: User[]; } permits = { manage: (ctx: Context): boolean => this.related.owners.includes(ctx.subject), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.permits.manage(ctx) } } class Publication implements Namespace { related: { organizations: Organization[]; editors: User[]; admins: User[]; } permits = { edit: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.permits.admin(ctx), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.admin(ctx)) } } class Article implements Namespace { related: { publications: Publication[]; collaborators: User[]; } permits = { view: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.permits.edit(ctx), edit: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.edit(ctx)) } }
With this permission model, the following relationships would be created: 1. Organization:org1#owners@User:123
- User:123 is an owner of Organization:org1 2. Publication:pub1#editors@User:456
- User:456 is an editor of Publication:pub1 3. Article:article1#collaborators@User:789
- User:789 is a collaborator on Article:article1 4. Publication:pub1#organizations@Organization:org1
- Publication:pub1 belongs to Organization:org1 5. Article:article1#publications@Publication:pub1
- Article:article1 belongs to Publication:pub1 In this scenario: - User:123 (organization owner) would have admin permissions on the organization, which cascade to admin permissions on all publications in that organization, and edit permissions on all articles in those publications. - User:456 (publication editor) would have edit permissions on the publication and all articles in that publication. - User:789 (article collaborator) would only have edit and view permissions on the specific article they're collaborating on. This model demonstrates how permissions can be inherited across namespaces using the traverse
method, which allows checking permissions across related objects [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model#define-permissions-in-the-opl). The permission checks would work transitively, so if you check whether User:123 can edit Article:article1, the system would traverse from the article to its publication, then to the organization, and find that User:123 is an owner of the organization, thus granting edit permission.careful-honey-27437
04/28/2025, 2:49 AMbrief-egg-61061
04/28/2025, 2:49 AMbrief-egg-61061
04/28/2025, 2:50 AMtypescript 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.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.admin(ctx), update: (ctx: Context): boolean => this.permits.admin(ctx), delete: (ctx: Context): boolean => this.permits.manage(ctx), manage: (ctx: Context): boolean => this.related.owners.includes(ctx.subject), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.permits.manage(ctx) } } class Publication implements Namespace { related: { organizations: Organization[]; editors: User[]; admins: User[]; } permits = { create: (ctx: Context): boolean => this.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.edit(ctx), update: (ctx: Context): boolean => this.permits.edit(ctx), delete: (ctx: Context): boolean => this.permits.admin(ctx), edit: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.permits.admin(ctx), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.admin(ctx)) } } class Article implements Namespace { related: { publications: Publication[]; collaborators: User[]; } permits = { create: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.edit(ctx)), read: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.permits.update(ctx), update: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.edit(ctx)), delete: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.admin(ctx)), update_status: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.editors.includes(ctx.subject) || pub.permits.admin(ctx)), view: (ctx: Context): boolean => this.permits.read(ctx), edit: (ctx: Context): boolean => this.permits.update(ctx) } }
In this updated model: 1. All namespaces now have standard CRUD (create, read, update, delete) permissions 2. The Article namespace has a special update_status
permission that can only be used by Publication editors or admins (not by Article collaborators) 3. I've maintained the existing role structure: - User:123 as Organization owner - User:456 as Publication editor - User:789 as Article collaborator With this permission model: - Organization owners can perform all operations on organizations, publications, and articles - Organization admins can create, read, and update organizations, publications, and articles, but cannot delete organizations - Publication editors can create, read, update articles, and update article status - Article collaborators can only read and update specific articles they're collaborating on, but cannot update the status The update_status
permission specifically checks if the subject is an editor of the publication the article belongs to, implementing the requirement that only publication editors can update article status. This model demonstrates how you can create fine-grained permissions that inherit across namespaces using the Ory Permission Language (OPL) [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model#define-permissions-in-the-opl).careful-honey-27437
04/28/2025, 2:51 AMedit
permit, only use an update
permit instead.brief-egg-61061
04/28/2025, 2:51 AMbrief-egg-61061
04/28/2025, 2:51 AMupdate
permits instead of edit
permits, while maintaining the same permission structure. typescript 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.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.admin(ctx), update: (ctx: Context): boolean => this.permits.admin(ctx), delete: (ctx: Context): boolean => this.permits.manage(ctx), manage: (ctx: Context): boolean => this.related.owners.includes(ctx.subject), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.permits.manage(ctx) } } class Publication implements Namespace { related: { organizations: Organization[]; editors: User[]; admins: User[]; } permits = { create: (ctx: Context): boolean => this.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.update(ctx), update: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.permits.admin(ctx), delete: (ctx: Context): boolean => this.permits.admin(ctx), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.admin(ctx)) } } class Article implements Namespace { related: { publications: Publication[]; collaborators: User[]; } permits = { create: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.update(ctx)), read: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.permits.update(ctx), update: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.update(ctx)), delete: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.admin(ctx)), update_status: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.editors.includes(ctx.subject) || pub.permits.admin(ctx)), view: (ctx: Context): boolean => this.permits.read(ctx) } }
In this updated model: 1. I've replaced all instances of the edit
permit with the update
permit 2. The Publication namespace now uses update
instead of edit
for the permission that editors have 3. All references to edit
in the traversal logic have been changed to update
4. The Article namespace's view
permit now references read
instead of edit
The permission hierarchy remains the same: - Organization owners and admins have full control over their organizations - Publication editors can update publications and articles - Article collaborators can update specific articles they're collaborating on - The special update_status
permission is still restricted to Publication editors This model demonstrates how you can create consistent permission naming conventions while maintaining the inheritance structure across namespaces using the Ory Permission Language (OPL) [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model#define-permissions-in-the-opl).careful-honey-27437
04/28/2025, 2:54 AMupdate_status
permission to a user called usr:789
careful-honey-27437
04/28/2025, 2:54 AMupdate_status
permission to a user called usr:789
brief-egg-61061
04/28/2025, 2:54 AMbrief-egg-61061
04/28/2025, 2:54 AMupdate_status
permission to a user called usr:789
in the Ory Permissions system. Based on our previous example, the update_status
permission in the Article namespace is only available to Publication editors or admins. To grant this permission to usr:789
, we need to make this user an editor of the relevant Publication. Here's how you can do it using the Ory CLI: bash # Add usr:789 as an editor of the Publication ory create relationships --file relationship.json
Where your relationship.json
file would contain: json { "namespace": "Publication", "object": "pub1", "relation": "editors", "subject_id": "usr:789" }
This creates the relationship Publication:pub1#editors@usr:789
, making usr:789
an editor of Publication:pub1
. Alternatively, you can update your OPL (Ory Permission Language) file and then update it in Ory Network using the CLI: bash ory update opl --file /path/to/namespace_config.ts
Or you can use the patch command: bash ory patch opl --file /path/to/namespace_config.ts
Both commands will update the Ory Permission Language file in Ory Network, and legacy namespace definitions will be overwritten [Ory update opl](https://www.ory.sh/docs/cli/ory-update-opl#ory-update-opl). You can then verify that usr:789
has the update_status
permission on a specific article by running: bash ory is allowed usr:789 update_status Article article1
If the relationship was created correctly and the permission model is set up as in our previous examples, this should return Allowed
[Get started with Ory Permissions](https://www.ory.sh/docs/guides/permissions/overview#checking-for-permissions). Remember that for this to work, you need to have the proper relationships in place: 1. Publication:pub1#editors@usr:789
(which we just created) 2. Article:article1#publications@Publication:pub1
(to connect the article to the publication) This approach leverages Ory's relationship-based permission model to grant specific permissions to users through their roles in related objects.careful-honey-27437
04/28/2025, 2:56 AMcareful-honey-27437
04/28/2025, 2:56 AMbrief-egg-61061
04/28/2025, 2:56 AMbrief-egg-61061
04/28/2025, 2:56 AMtypescript import { Configuration, PermissionApi, RelationshipApi } from '@ory/client' // Initialize the Ory client const ory = new Configuration({ basePath: '<https://your-project.projects.oryapis.com>', accessToken: 'your-api-token' }) // Create a relationship to make usr:789 an editor of Publication:pub1 async function addPublicationEditor() { try { await ory .relationship .createRelationship({ createRelationshipBody: { namespace: "Publication", object: "pub1", relation: "editors", subject_id: "usr:789" } }) console.log("Successfully added usr:789 as editor") } catch (error) { console.error("Error adding editor:", error) } } // Check if usr:789 has update_status permission on Article:article1 async function checkUpdateStatusPermission() { try { const response = await ory .permission .checkPermission({ namespace: "Article", object: "article1", relation: "update_status", subject_id: "usr:789" }) const isAllowed = response.data.allowed console.log(`User has update_status permission: ${isAllowed}`) return isAllowed } catch (error) { console.error("Error checking permission:", error) return false } } // Execute the functions async function main() { await addPublicationEditor() const hasPermission = await checkUpdateStatusPermission() if (hasPermission) { // Proceed with status update operation console.log("Updating article status...") } else { console.log("Permission denied") } } main()
Note that in the SDK V1, the API structure has been updated. The older SDK used write.createRelationTuple()
and read.getCheck()
, but the newer SDK uses relationship.createRelationship()
and permission.checkPermission()
as shown in the example above [SDK V1 upgrade guide](https://www.ory.sh/docs/guides/upgrade/sdk-v1#permissions--access-control). This code demonstrates how to: 1. Create a relationship to make a user an editor of a publication 2. Check if that user has the update_status
permission on an article 3. Conditionally execute business logic based on the permission check The SDK is available for multiple languages, but this example uses the TypeScript/JavaScript SDK which is compatible with Node.js, React.js, Angular, Vue.js, and other JavaScript frameworks [Software Development Kits (SDKs) for Ory](https://www.ory.sh/docs/sdk).careful-honey-27437
04/28/2025, 3:14 AMupdate
and update_status
permissions on art:abc
for user:123
brief-egg-61061
04/28/2025, 3:14 AMbrief-egg-61061
04/28/2025, 3:14 AMupdate
and update_status
permissions on art:abc
for user:123
using the Ory TypeScript SDK. typescript import { Configuration, PermissionApi } from '@ory/client' // Initialize the Ory client const ory = new Configuration({ basePath: '<https://your-project.projects.oryapis.com>', accessToken: 'your-api-token' }) // Check multiple permissions for user:123 on art:abc async function checkArticlePermissions() { try { // Check update permission const updateResponse = await ory .permission .checkPermission({ namespace: "Article", object: "art:abc", relation: "update", subject_id: "user:123" }) const canUpdate = updateResponse.data.allowed // Check update_status permission const statusResponse = await ory .permission .checkPermission({ namespace: "Article", object: "art:abc", relation: "update_status", subject_id: "user:123" }) const canUpdateStatus = statusResponse.data.allowed console.log(`User can update article: ${canUpdate}`) console.log(`User can update article status: ${canUpdateStatus}`) return { update: canUpdate, update_status: canUpdateStatus } } catch (error) { console.error("Error checking permissions:", error) return { update: false, update_status: false } } } // Execute the function and handle the results async function main() { const permissions = await checkArticlePermissions() if (permissions.update && permissions.update_status) { console.log("User has full editing capabilities") } else if (permissions.update) { console.log("User can update content but not status") } else if (permissions.update_status) { console.log("User can only update status") } else { console.log("User has no editing permissions") } } main()
Alternatively, you can use the batch check endpoint to check multiple permissions in a single API call: typescript async function batchCheckPermissions() { try { const response = await ory .permission .batchCheckPermission({ max_depth: 15, // Optional: specify max traversal depth body: { tuples: [ { namespace: "Article", object: "art:abc", relation: "update", subject_id: "user:123" }, { namespace: "Article", object: "art:abc", relation: "update_status", subject_id: "user:123" } ] } }) const results = response.data.results console.log("Batch permission check results:", results) return results } catch (error) { console.error("Error in batch permission check:", error) return [] } }
The batch check method is more efficient when you need to check multiple permissions at once [Ory API Reference](https://www.ory.sh/docs/reference/api). You can also perform these checks using the Ory CLI: bash # Check update permission ory is allowed user:123 update Article art:abc # Check update_status permission ory is allowed user:123 update_status Article art:abc
These commands will return either "Allowed" or "Denied" based on the permission check results [Get started with Ory Permissions](https://www.ory.sh/docs/guides/permissions/overview#checking-for-permissions).careful-honey-27437
04/28/2025, 3:15 AMupdate
and update_status
permissions using the TS SDK in one call. Use the batch check API.brief-egg-61061
04/28/2025, 3:15 AMbrief-egg-61061
04/28/2025, 3:15 AMupdate
and update_status
permissions in a single call using the batch check API with the Ory TypeScript SDK: typescript import { Configuration, PermissionApi } from '@ory/client' // Initialize the Ory client const ory = new Configuration({ basePath: '<https://your-project.projects.oryapis.com>', accessToken: 'your-api-token' }) // Check multiple permissions for user:123 on art:abc using batch API async function batchCheckArticlePermissions() { try { const response = await ory .permission .batchCheckPermission({ max_depth: 15, // Optional: specify max traversal depth body: { tuples: [ { namespace: "Article", object: "art:abc", relation: "update", subject_id: "user:123" }, { namespace: "Article", object: "art:abc", relation: "update_status", subject_id: "user:123" } ] } }) const results = response.data.results // Extract individual permission results const canUpdate = results[0].allowed const canUpdateStatus = results[1].allowed console.log(`User can update article: ${canUpdate}`) console.log(`User can update article status: ${canUpdateStatus}`) return { update: canUpdate, update_status: canUpdateStatus } } catch (error) { console.error("Error in batch permission check:", error) return { update: false, update_status: false } } } // Execute the function and handle the results async function main() { const permissions = await batchCheckArticlePermissions() if (permissions.update && permissions.update_status) { console.log("User has full editing capabilities") } else if (permissions.update) { console.log("User can update content but not status") } else if (permissions.update_status) { console.log("User can only update status") } else { console.log("User has no editing permissions") } } main()
This code uses the batchCheckPermission
method from the Ory TypeScript SDK to check both permissions in a single API call, which is more efficient than making separate requests [Ory API Reference](https://www.ory.sh/docs/reference/api). The batch API returns an array of results in the same order as the input tuples, allowing you to easily map each result to its corresponding permission check. Each result contains an allowed
boolean indicating whether the permission is granted, and potentially an error
string if there was an issue checking that specific permission [Ory API Reference](https://www.ory.sh/docs/keto/reference/rest-api).careful-honey-27437
04/28/2025, 3:20 AMbrief-egg-61061
04/28/2025, 3:20 AMbrief-egg-61061
04/28/2025, 3:20 AMPublicationUsage
namespace to our OPL file that can only be accessed by Organization owners and admins, and Publication editors and admins. typescript 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.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.admin(ctx), update: (ctx: Context): boolean => this.permits.admin(ctx), delete: (ctx: Context): boolean => this.permits.manage(ctx), manage: (ctx: Context): boolean => this.related.owners.includes(ctx.subject), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.permits.manage(ctx) } } class Publication implements Namespace { related: { organizations: Organization[]; editors: User[]; admins: User[]; } permits = { create: (ctx: Context): boolean => this.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.update(ctx), update: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.permits.admin(ctx), delete: (ctx: Context): boolean => this.permits.admin(ctx), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.admin(ctx)) } } class Article implements Namespace { related: { publications: Publication[]; collaborators: User[]; } permits = { create: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.update(ctx)), read: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.permits.update(ctx), update: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.update(ctx)), delete: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.admin(ctx)), update_status: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.editors.includes(ctx.subject) || pub.permits.admin(ctx)), view: (ctx: Context): boolean => this.permits.read(ctx) } } class PublicationUsage implements Namespace { related: { publications: Publication[]; } permits = { read: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.editors.includes(ctx.subject) || pub.related.admins.includes(ctx.subject) || pub.related.organizations.traverse((org) => org.related.owners.includes(ctx.subject) || org.related.admins.includes(ctx.subject) ) ), update: (ctx: Context): boolean => this.permits.read(ctx), delete: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.admins.includes(ctx.subject) || pub.related.organizations.traverse((org) => org.related.owners.includes(ctx.subject) || org.related.admins.includes(ctx.subject) ) ) } }
In this updated model, I've added a new PublicationUsage
namespace that: 1. Has a relationship with Publication
objects 2. Defines permissions that can only be accessed by specific roles: - read
and update
permissions are granted to: - Publication editors - Publication admins - Organization owners - Organization admins - delete
permission is more restricted and only granted to: - Publication admins - Organization owners - Organization admins The permissions are defined using the traverse
method to check relationships across namespaces, which is a key feature of the Ory Permission Language [What is the Ory Permission Language](https://www.ory.sh/blog/what-is-the-ory-permission-language). To use this updated OPL file, you would save it and then update it in Ory Network using the CLI: bash ory update opl --file /path/to/namespace_config.ts
This follows the naming conventions for namespaces in Ory Permissions, where namespaces should be named in singular form with upper camel case [Namespaces](https://www.ory.sh/docs/keto/concepts/namespaces).careful-honey-27437
04/28/2025, 3:20 AMbrief-egg-61061
04/28/2025, 3:20 AMbrief-egg-61061
04/28/2025, 3:21 AMtypescript 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.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.admin(ctx), update: (ctx: Context): boolean => this.permits.admin(ctx), delete: (ctx: Context): boolean => this.permits.manage(ctx), manage: (ctx: Context): boolean => this.related.owners.includes(ctx.subject), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.permits.manage(ctx) } } class Publication implements Namespace { related: { organizations: Organization[]; editors: User[]; admins: User[]; } permits = { create: (ctx: Context): boolean => this.permits.admin(ctx), read: (ctx: Context): boolean => this.permits.update(ctx), update: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.permits.admin(ctx), delete: (ctx: Context): boolean => this.permits.admin(ctx), admin: (ctx: Context): boolean => this.related.admins.includes(ctx.subject) || this.related.organizations.traverse((org) => org.permits.admin(ctx)) } } class Article implements Namespace { related: { publications: Publication[]; collaborators: User[]; } permits = { create: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.update(ctx)), read: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.permits.update(ctx), update: (ctx: Context): boolean => this.related.collaborators.includes(ctx.subject) || this.related.publications.traverse((pub) => pub.permits.update(ctx)), delete: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.permits.admin(ctx)), update_status: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.editors.includes(ctx.subject) || pub.permits.admin(ctx) ), view: (ctx: Context): boolean => this.permits.read(ctx) } } class PublicationUsage implements Namespace { related: { publications: Publication[]; } permits = { read: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.editors.includes(ctx.subject) || pub.related.admins.includes(ctx.subject) || pub.related.organizations.traverse((org) => org.related.owners.includes(ctx.subject) || org.related.admins.includes(ctx.subject) ) ), update: (ctx: Context): boolean => this.permits.read(ctx), delete: (ctx: Context): boolean => this.related.publications.traverse((pub) => pub.related.admins.includes(ctx.subject) || pub.related.organizations.traverse((org) => org.related.owners.includes(ctx.subject) || org.related.admins.includes(ctx.subject) ) ) } }
I've improved the formatting by: 1. Ensuring consistent indentation throughout the file 2. Adding proper line breaks for better readability in complex permission checks 3. Properly nesting the traverse
method calls with consistent indentation 4. Maintaining consistent spacing around operators and parentheses This formatting follows TypeScript best practices and makes the permission model easier to read and maintain [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model).careful-honey-27437
04/28/2025, 3:29 AMbrief-egg-61061
04/28/2025, 3:29 AMbrief-egg-61061
04/28/2025, 3:30 AMtypescript import { Configuration } from '@ory/client' // Initialize the Ory client const ory = new Configuration({ basePath: '<https://your-project.projects.oryapis.com>', accessToken: 'your-api-token' }) // List of all permits to check for the Article namespace const permitsToCheck = [ 'create', 'read', 'update', 'delete', 'update_status', 'view' ] // Check all permits for user:123 on art:abc async function listUserPermits() { try { const response = await ory .permission .batchCheckPermission({ body: { tuples: permitsToCheck.map(permit => ({ namespace: "Article", object: "art:abc", relation: permit, subject_id: "user:123" })) } }) const results = response.data.results // Create a map of permit to allowed status const userPermits = permitsToCheck.reduce((acc, permit, index) => { acc[permit] = results[index].allowed return acc }, {}) console.log("User permits for art:abc:", userPermits) // Filter to only show allowed permits const allowedPermits = Object.entries(userPermits) .filter(([_, allowed]) => allowed) .map(([permit, _]) => permit) console.log("Allowed permits:", allowedPermits) return userPermits } catch (error) { console.error("Error checking permits:", error) return {} } } // Execute the function listUserPermits()
This code: 1. Defines a list of all permits we want to check based on our Article namespace 2. Uses the batch check API to check all permits in a single API call 3. Processes the results to create a map of permit names to their allowed status 4. Filters the results to show only the permits that are allowed The batch check API is more efficient than making separate requests for each permit [Ory API Reference](https://www.ory.sh/docs/reference/api). Note that this approach requires you to know all the possible permits in advance. If your permission model changes, you'll need to update the permitsToCheck
array accordingly. If you need to discover what permits are available for a namespace, you would need to examine your OPL (Ory Permission Language) configuration, as there's no API to dynamically discover all available permits for a namespace. </assistant_answer>careful-honey-27437
04/28/2025, 3:31 AMbrief-egg-61061
04/28/2025, 3:31 AMbrief-egg-61061
04/28/2025, 3:31 AMgetRelationships
API endpoint or the Ory CLI's list relationships
command. Here's how to do it using the TypeScript SDK: typescript import { Configuration } from '@ory/client' // Initialize the Ory client const ory = new Configuration({ basePath: '<https://your-project.projects.oryapis.com>', accessToken: 'your-api-token' }) async function listUserRelations() { try { // Query for relationships where user:123 is the subject and art:abc is the object const response = await ory .relationship .getRelationships({ subject_id: "user:123", object: "art:abc", namespace: "Article" // Optional: filter by namespace }) const relationships = response.data.relation_tuples console.log("User relationships with art:abc:", relationships) // Extract just the relations const relations = relationships.map(rel => rel.relation) console.log("Relations:", relations) return relationships } catch (error) { console.error("Error fetching relationships:", error) return [] } } // Execute the function listUserRelations()
Alternatively, you can use the Ory CLI: bash ory list relationships --subject-id user:123 --object art:abc --namespace Article
This command will return all relationships where user:123 is the subject and art:abc is the object in the Article namespace [Ory list relationships](https://www.ory.sh/docs/cli/ory-list-relationships#ory-list-relationships). The API response will include all direct relationships between the user and the article. For example, if user:123 is a collaborator on art:abc, you would see a relationship like: Article:art:abc#collaborators@user:123
Note that this API only returns direct relationships, not permissions that might be inherited through other relationships (like being an editor of a publication that the article belongs to). To get a complete picture of all permissions, you would still need to use the batch check API as shown in our previous examples. The relationships API supports pagination with the page-size
and page-token
parameters if you have many relationships to list [Ory API Reference](https://www.ory.sh/docs/reference/api).