<@U04UQ68083H> Can permissions be inherited across...
# ory-copilot
c
@chilly-king-10285 Can permissions be inherited across namespaces? For instance, if you have three namespaces, Organization, Publication, and Article, could access to access to article:123 be inferred because user:456 has the “admin” role for organization:789?
b
A message from kapa.ai
Yes, permissions can be inherited across namespaces in Ory Permissions. This is one of the key features of Ory's implementation of Google Zanzibar's permission model. In your example with Organization, Publication, and Article namespaces, you can define relationships and permission rules that allow access to an article to be inferred based on a user's role in an organization. This is achieved through the Ory Permission Language (OPL), which allows you to define transitive relationships and permission checks. For example, you could define:
Copy code
typescript 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.
c
Update the example to illustrate how Organizations can have owners and admins, Publications can have editors and admins, and Articles can have collaborators.
pdate the example to illustrate how Organizations can have owners and admins, Publications can have editors and admins, and Articles can have collaborators. Use the example where user:123 is an Organization owner, user:456 is a Publication editor, and user:789 is an Article collaborator.
@chilly-king-10285 Update the example to illustrate how Organizations can have owners and admins, Publications can have editors and admins, and Articles can have collaborators. Use the example where user:123 is an Organization owner, user:456 is a Publication editor, and user:789 is an Article collaborator.
b
A message from kapa.ai
I'll update the example to illustrate the hierarchical permission structure with different roles across the Organization, Publication, and Article namespaces.
Copy code
typescript 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.
c
@chilly-king-10285 Update the example to include create, read, update, and delete permissions. Also, add another permission called update_status under the Article namespace that only Publication editors can update.
b
A message from kapa.ai
I'll update the example to include CRUD permissions and add the special update_status permission for Publication editors.
Copy code
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.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).
c
@chilly-king-10285 Update the example again, but don’t use the
edit
permit, only use an
update
permit instead.
b
A message from kapa.ai
I'll update the example to use only
update
permits instead of
edit
permits, while maintaining the same permission structure.
Copy code
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).
c
Show how to grant
update_status
permission to a user called
usr:789
@chilly-king-10285 Show how to grant
update_status
permission to a user called
usr:789
b
A message from kapa.ai
I'll show you how to grant the
update_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:
Copy code
bash # Add usr:789 as an editor of the Publication ory create relationships --file relationship.json
Where your
relationship.json
file would contain:
Copy code
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:
Copy code
bash ory update opl --file /path/to/namespace_config.ts
Or you can use the patch command:
Copy code
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:
Copy code
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.
c
Show the TS SDK
@chilly-king-10285 Show the example using the TS SD
b
A message from kapa.ai
I'll show you how to use the TypeScript SDK to check permissions and manage relationships in Ory Permissions based on our previous example. Here's how you would use the Ory TypeScript SDK to create relationships and check permissions:
Copy code
typescript 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).
c
@chilly-king-10285 Show how to check both the
update
and
update_status
permissions on
art:abc
for
user:123
b
A message from kapa.ai
I'll show you how to check both the
update
and
update_status
permissions on
art:abc
for
user:123
using the Ory TypeScript SDK.
Copy code
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:
Copy code
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:
Copy code
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).
c
@chilly-king-10285 Show how to check both
update
and
update_status
permissions using the TS SDK in one call. Use the batch check API.
b
A message from kapa.ai
I'll show you how to check both
update
and
update_status
permissions in a single call using the batch check API with the Ory TypeScript SDK:
Copy code
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).
c
@chilly-king-10285 Add a new Namespace to the OPL file called Publication-Usage that only be accessed by Organization owners and admins, and Publication editors and admins.
b
A message from kapa.ai
I'll add a new
PublicationUsage
namespace to our OPL file that can only be accessed by Organization owners and admins, and Publication editors and admins.
Copy code
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:
Copy code
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).
c
@chilly-king-10285 Fix the code formatting
b
A message from kapa.ai
I'll fix the code formatting for the OPL file we've been working on:
Copy code
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)         )       )   } }
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).
c
@chilly-king-10285 Using the TS SDK, show how to list all permits that user:123 has for art:abc.
b
A message from kapa.ai
I'll show you how to use the TypeScript SDK to list all permits that user:123 has for art:abc in Ory Permissions. Unfortunately, there isn't a direct API method to list all permits a user has for a specific object in a single call. Instead, we need to check each permission individually or use the batch check API to check multiple permissions at once. Here's how you can check all the permits defined in our Article namespace for user:123 on art🔤
Copy code
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' })  // 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>
c
@chilly-king-10285 Use the relationships api to list all the relations user:123 has for art:abc
b
A message from kapa.ai
I'll show you how to use the Ory Relationships API to list all the relations that user:123 has for art:abc. To list relationships between a subject and an object in Ory Permissions, you can use the
getRelationships
API endpoint or the Ory CLI's
list relationships
command. Here's how to do it using the TypeScript SDK:
Copy code
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:
Copy code
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:
Copy code
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).