quick-toddler-60155
12/28/2024, 9:34 PMNamespace:<uuid>#relation@(Namespace:<uuid>#relation) into OPL. To be more specific, what I want to create is this permission in OPL: User:<user_uuid>#view@(Group:<group_uuid>#members) , i.e. members of <group_uuid> should be able to view the user <user_uuid>. Can you help me with this?chilly-king-10285
12/28/2024, 9:34 PMchilly-king-10285
12/28/2024, 9:34 PMUser:<user_uuid>#view@(Group:<group_uuid>#members) into Ory Permission Language (OPL), we need to define the appropriate namespaces and relationships. Here's how you can represent this permission in OPL: typescript import { Namespace, SubjectSet, Context } from "@ory/keto-namespace-types" class User implements Namespace { related: { viewers: (User | SubjectSet<Group, "members">)[] } permits = { view: (ctx: Context): boolean => this.related.viewers.includes(ctx.subject) } } class Group implements Namespace { related: { members: User[] } } Let's break down this OPL code: 1. We define two namespaces: User and Group. 2. In the User namespace, we define a viewers relation that can include either individual `User`s or members of a Group (using SubjectSet<Group, "members">). 3. We define a view permission in the User namespace that checks if the subject (the one trying to view) is included in the viewers relation. 4. The Group namespace has a members relation that includes `User`s. To set up the specific permission you mentioned (User:<user_uuid>#view@(Group:<group_uuid>#members)), you would need to create the following relationship using the Ory CLI or API: User:<user_uuid>#viewers@Group:<group_uuid>#members This relationship says that the members of Group:<group_uuid> are viewers of User:<user_uuid>. When a permission check is performed, Ory Permissions will use this OPL configuration to determine if a user has the view permission on the specified user object. For more information on creating permission models and using the Ory Permission Language, you can refer to the [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model) guide in the Ory documentation.quick-toddler-60155
12/28/2024, 9:48 PMchilly-king-10285
12/28/2024, 9:51 PMchilly-king-10285
12/28/2024, 9:52 PMtypescript
import { Namespace, Context } from "@ory/keto-namespace-types"
class User implements Namespace {
related: {
admins: User[]
managers: User[]
}
permits = {
viewAllAttributes: (ctx: Context): boolean =>
this.related.admins.includes(ctx.subject),
editAllAttributes: (ctx: Context): boolean =>
this.related.admins.includes(ctx.subject),
viewBasicAttributes: (ctx: Context): boolean =>
ctx.subject.equals(this) ||
this.related.managers.includes(ctx.subject) ||
this.permits.viewAllAttributes(ctx),
editBasicAttributes: (ctx: Context): boolean =>
ctx.subject.equals(this) ||
this.permits.editAllAttributes(ctx),
viewSensitiveAttributes: (ctx: Context): boolean =>
this.related.managers.includes(ctx.subject) ||
this.permits.viewAllAttributes(ctx),
editSensitiveAttributes: (ctx: Context): boolean =>
this.permits.editAllAttributes(ctx)
}
}
class Attribute implements Namespace {
related: {
owner: User[]
type: AttributeType[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.type.traverse((t) => t.permits.view(ctx)) &&
this.related.owner.traverse((o) =>
o.permits.viewBasicAttributes(ctx) ||
o.permits.viewSensitiveAttributes(ctx) ||
o.permits.viewAllAttributes(ctx)
),
edit: (ctx: Context): boolean =>
this.related.type.traverse((t) => t.permits.edit(ctx)) &&
this.related.owner.traverse((o) =>
o.permits.editBasicAttributes(ctx) ||
o.permits.editSensitiveAttributes(ctx) ||
o.permits.editAllAttributes(ctx)
)
}
}
class AttributeType implements Namespace {
related: {
viewableBy: (User | SubjectSet<User, "managers">)[]
editableBy: (User | SubjectSet<User, "managers">)[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewableBy.includes(ctx.subject),
edit: (ctx: Context): boolean =>
this.related.editableBy.includes(ctx.subject)
}
}
This OPL model addresses the requirements as follows:
1. Users have several attributes with different permissions:
- We define an Attribute namespace that represents user attributes.
- Each Attribute is linked to its owner (User) and has a type (AttributeType).
2. Users can see and edit some attributes, see but not edit some, and not see some at all:
- The User namespace has different permission functions (viewBasicAttributes, editBasicAttributes, viewSensitiveAttributes, etc.) to represent these varying levels of access.
- The Attribute namespace checks these permissions when determining if a user can view or edit an attribute.chilly-king-10285
12/28/2024, 9:52 PMtypescript import { Namespace, Context } from "@ory/keto-namespace-types" class User implements Namespace { related: { admins: User[] managers: User[] } permits = { viewAllAttributes: (ctx: Context): boolean => this.related.admins.includes(ctx.subject), editAllAttributes: (ctx: Context): boolean => this.related.admins.includes(ctx.subject), viewBasicAttributes: (ctx: Context): boolean => ctx.subject.equals(this) || this.related.managers.includes(ctx.subject) || this.permits.viewAllAttributes(ctx), editBasicAttributes: (ctx: Context): boolean => ctx.subject.equals(this) || this.permits.editAllAttributes(ctx), viewSensitiveAttributes: (ctx: Context): boolean => this.related.managers.includes(ctx.subject) || this.permits.viewAllAttributes(ctx), editSensitiveAttributes: (ctx: Context): boolean => this.permits.editAllAttributes(ctx) } } class Attribute implements Namespace { related: { owner: User[] type: AttributeType[] } permits = { view: (ctx: Context): boolean => this.related.type.traverse((t) => t.permits.view(ctx)) && this.related.owner.traverse((o) => o.permits.viewBasicAttributes(ctx) || o.permits.viewSensitiveAttributes(ctx) || o.permits.viewAllAttributes(ctx) ), edit: (ctx: Context): boolean => this.related.type.traverse((t) => t.permits.edit(ctx)) && this.related.owner.traverse((o) => o.permits.editBasicAttributes(ctx) || o.permits.editSensitiveAttributes(ctx) || o.permits.editAllAttributes(ctx) ) } } class AttributeType implements Namespace { related: { viewableBy: (User | SubjectSet<User, "managers">)[] editableBy: (User | SubjectSet<User, "managers">)[] } permits = { view: (ctx: Context): boolean => this.related.viewableBy.includes(ctx.subject), edit: (ctx: Context): boolean => this.related.editableBy.includes(ctx.subject) } } This OPL model addresses the requirements as follows: 1. Users have several attributes with different permissions: - We define an Attribute namespace that represents user attributes. - Each Attribute is linked to its owner (User) and has a type (AttributeType). 2. Users can see and edit some attributes, see but not edit some, and not see some at all: - The User namespace has different permission functions (viewBasicAttributes, editBasicAttributes, viewSensitiveAttributes, etc.) to represent these varying levels of access. - The Attribute namespace checks these permissions when determining if a user can view or edit an attribute. 3. Admin users can see and edit all attributes: - The User namespace has viewAllAttributes and editAllAttributes permissions that are granted to admins. 4. Users with a specific relation (managers) can see and edit some attributes: - We define a managers relation in the User namespace. - The AttributeType namespace allows for fine-grained control over which users or managers can view or edit specific types of attributes. To use this model, you would need to: 1. Create relationships to define admins and managers for each user. 2. Create relationships to define the owner and type for each attribute. 3. Create relationships to define which users or managers can view or edit each attribute type. When checking permissions, you would use the view and edit permissions on the Attribute namespace. This model provides a flexible framework for handling complex attribute-level permissions. You can further customize it based on your specific needs, such as adding more granular permission levels or additional relationships. For more information on creating and using permission models with Ory Permissions, refer to the [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model) guide in the Ory documentation.quick-toddler-60155
12/28/2024, 10:17 PMTo use this model, you would need to:
1. Create relationships to define admins and managers for each user.
2. Create relationships to define the owner and type for each attribute.
3. Create relationships to define which users or managers can view or edit each attribute type.
When checking permissions, you would use theandviewpermissions on theeditnamespace.Attribute
chilly-king-10285
12/28/2024, 10:17 PMchilly-king-10285
12/28/2024, 10:18 PMUser:<user_id>#admins@User:<admin_id> User:<user_id>#managers@User:<manager_id> For example, to make User:123 an admin of User456 User:456#admins@User:123 2. Creating relationships to define the owner and type for each attribute: For each attribute, you need to define its owner and type: Attribute:<attribute_id>#owner@User:<user_id> Attribute:<attribute_id>#type@AttributeType:<type_id> For example, to set User:456 as the owner of Attribute:789 and define it as a "basic" attribute type: Attribute:789#owner@User:456 Attribute:789#type@AttributeType:basic 3. Creating relationships to define which users or managers can view or edit each attribute type: To define permissions for attribute types: AttributeType:<type_id>#viewableBy@User:<user_id> AttributeType:<type_id>#editableBy@User:<user_id> AttributeType:<type_id>#viewableBy@User:<user_id>#managers AttributeType:<type_id>#editableBy@User:<user_id>#managers For example, to allow User:123 and managers of User:456 to view the "basic" attribute type: AttributeType:basic#viewableBy@User:123 AttributeType:basic#viewableBy@User:456#managers You can create these relationships using the Ory CLI. Here's an example of how to create a relationship: ory create relationship 'User:456#admins@User:123' When checking permissions, you would use the view and edit permissions on the Attribute namespace. This can be done using the Ory CLI or API. Here's an example using the CLI: To check if User:123 can view Attribute789 ory check permission User:123 view Attribute:789 To check if User:123 can edit Attribute789 ory check permission User:123 edit Attribute:789 These commands will return whether the permission is allowed or denied based on the relationships you've set up and the permission model you've defined. For more detailed information on creating relationships and checking permissions, you can refer to the [How to integrate permission checks to your application](https://www.ory.sh/docs/keto/guides/simple-access-check-guide) guide in the Ory documentation. Remember that this is a complex permission model, and you may need to adjust it based on your specific requirements. Always test your permission checks thoroughly to ensure they behave as expected.quick-toddler-60155
12/28/2024, 10:26 PMchilly-king-10285
12/28/2024, 10:30 PMchilly-king-10285
12/28/2024, 10:30 PMtypescript
import { Namespace, SubjectSet, Context } from "@ory/keto-namespace-types"
class User implements Namespace {
related: {
attributes: Attribute[]
}
}
class Group implements Namespace {
related: {
members: User[]
}
}
class Attribute implements Namespace {
related: {
owner: User[]
category: AttributeCategory[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.category.traverse((c) => c.permits.view(ctx)) ||
this.related.owner.traverse((o) => o.equals(ctx.subject)) ||
SubjectSet.from(Group, "members", "admins").includes(ctx.subject),
edit: (ctx: Context): boolean =>
this.related.category.traverse((c) => c.permits.edit(ctx)) ||
this.related.owner.traverse((o) => o.equals(ctx.subject)) ||
SubjectSet.from(Group, "members", "admins").includes(ctx.subject)
}
}
class AttributeCategory implements Namespace {
related: {
viewers: (User | SubjectSet<Group, "members">)[]
editors: (User | SubjectSet<Group, "members">)[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject)
}
}
In this revised model:
1. We've introduced a Group namespace to handle admin permissions.
2. The Attribute namespace now checks if the subject is a member of the "admins" group for both view and edit permissions.
3. We've replaced AttributeType with AttributeCategory to group similar attributes, reducing the number of permission checks.
To set up this system:
1. Create an admin group:
Group:admins#members@User:<admin_user_id>
2. Assign attributes to users:
User:<user_id>#attributes@Attribute:<attribute_id>
3. Categorize attributes:
Attribute:<attribute_id>#category@AttributeCategory:<category_id>chilly-king-10285
12/28/2024, 10:30 PMtypescript import { Namespace, SubjectSet, Context } from "@ory/keto-namespace-types" class User implements Namespace { related: { attributes: Attribute[] } } class Group implements Namespace { related: { members: User[] } } class Attribute implements Namespace { related: { owner: User[] category: AttributeCategory[] } permits = { view: (ctx: Context): boolean => this.related.category.traverse((c) => c.permits.view(ctx)) || this.related.owner.traverse((o) => o.equals(ctx.subject)) || SubjectSet.from(Group, "members", "admins").includes(ctx.subject), edit: (ctx: Context): boolean => this.related.category.traverse((c) => c.permits.edit(ctx)) || this.related.owner.traverse((o) => o.equals(ctx.subject)) || SubjectSet.from(Group, "members", "admins").includes(ctx.subject) } } class AttributeCategory implements Namespace { related: { viewers: (User | SubjectSet<Group, "members">)[] editors: (User | SubjectSet<Group, "members">)[] } permits = { view: (ctx: Context): boolean => this.related.viewers.includes(ctx.subject), edit: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) } } In this revised model: 1. We've introduced a Group namespace to handle admin permissions. 2. The Attribute namespace now checks if the subject is a member of the "admins" group for both view and edit permissions. 3. We've replaced AttributeType with AttributeCategory to group similar attributes, reducing the number of permission checks. To set up this system: 1. Create an admin group: Group:admins#members@User:<admin_user_id> 2. Assign attributes to users: User:<user_id>#attributes@Attribute:<attribute_id> 3. Categorize attributes: Attribute:<attribute_id>#category@AttributeCategory:<category_id> 4. Set up category permissions: AttributeCategory:<category_id>#viewers@Group:<group_id>#members AttributeCategory:<category_id>#editors@Group:<group_id>#members This approach scales better because: - It reduces the number of individual relationships by using groups and categories. - It simplifies admin management by using a single admin group. - It leverages Ory Permissions' built-in optimizations for handling large-scale permission checks. For more information on creating scalable permission models with Ory Permissions, you can refer to the [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model) guide in the Ory documentation. Remember, while this model is more scalable, you should still monitor performance as your system grows and adjust as needed. Ory Permissions is designed to handle large-scale scenarios, but proper modeling and optimization are key to achieving the best performance.quick-toddler-60155
12/31/2024, 2:27 AMchilly-king-10285
12/31/2024, 2:27 AMchilly-king-10285
12/31/2024, 2:27 AMtypescript import { Namespace, Context } from "@ory/keto-namespace-types" class User implements Namespace { related: { roles: Role[] } } class Role implements Namespace { related: { members: User[] } permits = { access: (ctx: Context): boolean => this.related.members.includes(ctx.subject) } } class Resource implements Namespace { related: { viewers: Role[] editors: Role[] } permits = { view: (ctx: Context): boolean => this.related.viewers.traverse((role) => role.permits.access(ctx)) || this.permits.edit(ctx), edit: (ctx: Context): boolean => this.related.editors.traverse((role) => role.permits.access(ctx)) } } This OPL model defines three namespaces: User, Role, and Resource. Here's how to use this model to implement your role-based access system: 1. Create roles for admins, teachers, students, and parents: Role:admin Role:teacher Role:student Role:parent 2. Assign users to roles: Role:admin#members@User:<admin_id> Role:teacher#members@User:<teacher_id> Role:student#members@User:<student_id> Role:parent#members@User:<parent_id> 3. Define resources and their permissions: Resource:<resource_id>#viewers@Role:student Resource:<resource_id>#viewers@Role:parent Resource:<resource_id>#editors@Role:teacher Resource:<resource_id>#editors@Role:admin You can create these relationships using the Ory CLI. For example: ory create relationship 'Role:admin#members@User:123' ory create relationship 'Resource:456#editors@Role:admin' To check permissions, you would use the view and edit permissions on the Resource namespace. For example: ory check permission User:123 view Resource:456 ory check permission User:123 edit Resource:456 This model allows for flexible role-based access control: - Admins have full access (view and edit) to all resources. - Teachers can view and edit resources. - Students and parents can view resources but not edit them. - You can easily add or remove users from roles, or change resource permissions for different roles. For more detailed information on implementing role-based access control with Ory Keto, you can refer to the [Role Based Access Control (RBAC)](https://www.ory.sh/docs/keto/guides/rbac) guide in the Ory documentation. Remember that this is a basic implementation. Depending on your specific requirements, you might need to add more granular permissions or additional relationships between users and resources. [Role Based Access Control (RBAC)](https://www.ory.sh/docs/keto/guides/rbac)quick-toddler-60155
12/31/2024, 2:30 AMResource:<resource_id>#viewers@Role:student
Resource:<resource_id>#viewers@Role:parent
Resource:<resource_id>#editors@Role:teacher
Resource:<resource_id>#editors@Role:admin
2. Now imagine that one of the resources to be accessed is information about users - for example a certain student might have UUID 9b0ec9fa-f661-4371-b36e-26736f2604fc and given_name Greger. This information should be accessible to the student's parents, but not any parent.chilly-king-10285
12/31/2024, 2:30 AMchilly-king-10285
12/31/2024, 2:31 AMResource:<resource_id>#viewers@Role:student This means that users with the "student" role have viewing permission for the resource identified by <resource_id>. Resource:<resource_id>#viewers@Role:parent This grants viewing permission to users with the "parent" role for the same resource. Resource:<resource_id>#editors@Role:teacher This gives editing permission to users with the "teacher" role. Resource:<resource_id>#editors@Role:admin This grants editing permission to users with the "admin" role. These relationships define which roles have what type of access (viewing or editing) to a specific resource. 2. For the scenario where you want to restrict access to a student's information to only their parents, we need to modify our permission model. The previous model was too broad, allowing all parents to view all student resources. Here's how we can refine it: typescript import { Namespace, Context } from "@ory/keto-namespace-types" class User implements Namespace { related: { roles: Role[] parents: User[] } permits = { viewInfo: (ctx: Context): boolean => ctx.subject.equals(this) || this.related.parents.includes(ctx.subject) || Role.admin.permits.access(ctx) } } class Role implements Namespace { related: { members: User[] } permits = { access: (ctx: Context): boolean => this.related.members.includes(ctx.subject) } } In this refined model: - We've added a parents relation to the User namespace. - We've defined a viewInfo permission directly on the User namespace. To set up the relationships for a student and their parents: User:9b0ec9fa-f661-4371-b36e-26736f2604fc#parents@User:<parent1_id> User:9b0ec9fa-f661-4371-b36e-26736f2604fc#parents@User:<parent2_id> To check if a user can view Greger's information: ory check permission User:<user_id> viewInfo User:9b0ec9fa-f661-4371-b36e-26736f2604fc This setup ensures that: - The student can view their own information. - Only the specific parents of the student can view the student's information. - Admins can view all student information. - Other parents cannot view this student's information. This approach provides more granular control over access to student information, ensuring that only the appropriate parents can view a specific student's details. For more information on creating and managing relationships in Ory Keto, you can refer to the [Relationships](https://www.ory.sh/docs/keto/concepts/relation-tuples) concept in the Ory documentation. [Relationships](https://www.ory.sh/docs/keto/concepts/relation-tuples)quick-toddler-60155
12/31/2024, 2:36 AMctx.subject.equals(this) ||
Role.admin.permits.access(ctx)chilly-king-10285
12/31/2024, 2:36 AMchilly-king-10285
12/31/2024, 2:37 AM/admin/relation-tuples endpoint. Here's an example of how to create the relationships for a student and their parents using HTTP requests: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "User", "object": "9b0ec9fa-f661-4371-b36e-26736f2604fc", "relation": "parents", "subject_id": "<parent1_id>" } And for the second parent: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "User", "object": "9b0ec9fa-f661-4371-b36e-26736f2604fc", "relation": "parents", "subject_id": "<parent2_id>" } These HTTP requests would create the relationships we discussed earlier. For more details on using the REST API, you can refer to the [Ory Keto API Reference](https://www.ory.sh/docs/keto/reference/rest-api). 2. You're absolutely correct, and I apologize for the confusion. The OPL syntax I provided earlier is indeed not valid. Thank you for pointing this out. Let me provide a corrected version of the User namespace: typescript class User implements Namespace { related: { roles: Role[] parents: User[] } permits = { viewInfo: (ctx: Context): boolean => this.equals(ctx.subject) || this.related.parents.includes(ctx.subject) || this.related.roles.traverse((role) => role.name === "admin") } } In this corrected version: - We use this.equals(ctx.subject) to check if the subject is the user themselves. - We check if the subject is included in the user's parents. - We traverse the user's roles to check if they have an admin role. This syntax is more in line with the Ory Permission Language specification. For more details on the correct OPL syntax, you can refer to the [Ory Permission Language specification](https://www.ory.sh/docs/keto/reference/ory-permission-language) in the Ory documentation. Thank you for your attention to detail. It's important to have syntactically correct OPL to ensure proper functioning of the permission system. [Ory Keto API Reference](https://www.ory.sh/docs/keto/reference/rest-api) [Ory Permission Language specification](https://www.ory.sh/docs/keto/reference/ory-permission-language)quick-toddler-60155
12/31/2024, 3:12 PMmembers of the Group with uuid 13872e72-06bb-48b7-988d-d842adb18522 can view members of the Group with uuid ebbd52bc-fc62-4336-a051-0d76a1467ac0 .chilly-king-10285
12/31/2024, 3:12 PMchilly-king-10285
12/31/2024, 3:13 PMGroup:13872e72-06bb-48b7-988d-d842adb18522 Group:ebbd52bc-fc62-4336-a051-0d76a1467ac0 2. Then, create a relationship that says "Group A can view Group B": Group:ebbd52bc-fc62-4336-a051-0d76a1467ac0#viewers@Group:13872e72-06bb-48b7-988d-d842adb18522 3. In your OPL, you would define the permission model like this: typescript class Group implements Namespace { related: { members: User[] viewers: Group[] } permits = { view: (ctx: Context): boolean => this.related.viewers.traverse((group) => group.related.members.includes(ctx.subject)) } } class User implements Namespace {} This OPL configuration says that a user can view a group if they are a member of any group that is in the viewers relation of the target group. 4. To create these relationships using the HTTP API, you would make the following calls: For the group viewing permission: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Group", "object": "ebbd52bc-fc62-4336-a051-0d76a1467ac0", "relation": "viewers", "subject_id": "Group:13872e72-06bb-48b7-988d-d842adb18522" } For adding members to groups: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Group", "object": "13872e72-06bb-48b7-988d-d842adb18522", "relation": "members", "subject_id": "<user_id>" } With this setup, any member of the group 13872e72-06bb-48b7-988d-d842adb18522 will be able to view the members of the group ebbd52bc-fc62-4336-a051-0d76a1467ac0. This approach allows you to effectively model the relationship you described, where members of one group can view members of another group, using Ory Keto's relation tuple system and OPL. For more information on subject sets and complex permission modeling in Ory Keto, you can refer to the [Subjects](https://www.ory.sh/docs/keto/concepts/subjects) concept documentation. [Subjects](https://www.ory.sh/docs/keto/concepts/subjects)quick-toddler-60155
12/31/2024, 3:27 PMResource with uuid 23a43c32-3f5b-4df7-a8d7-50147f4f1811 that the User with uuid 890bd6d1-fde0-40c2-aece-a0521e4199fe should be permitted to view and edit and that members of the group 13872e72-06bb-48b7-988d-d842adb18522 should be able to view .chilly-king-10285
12/31/2024, 3:27 PMchilly-king-10285
12/31/2024, 3:27 PMResource namespace:
typescript
import { Namespace, SubjectSet, Context } from "@ory/keto-namespace-types"
class User implements Namespace {}
class Group implements Namespace {
related: {
members: User[]
}
}
class Resource implements Namespace {
related: {
viewers: (User | SubjectSet<Group, "members">)[]
editors: User[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject)
}
}
2. Now, let's create the necessary relationships using the HTTP API. We'll need to make several API calls:
a. Give the user permission to view and edit the resource:
http
PUT /admin/relation-tuples
Content-Type: application/json
{
"namespace": "Resource",
"object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811",
"relation": "viewers",
"subject_id": "User:890bd6d1-fde0-40c2-aece-a0521e4199fe"
}
http
PUT /admin/relation-tuples
Content-Type: application/json
{
"namespace": "Resource",
"object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811",
"relation": "editors",
"subject_id": "User:890bd6d1-fde0-40c2-aece-a0521e4199fe"
}
b. Give the group members permission to view the resource:
http
PUT /admin/relation-tuples
Content-Type: application/json
{
"namespace": "Resource",
"object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811",
"relation": "viewers",
"subject_set": {
"namespace": "Group",
"object": "13872e72-06bb-48b7-988d-d842adb18522",
"relation": "members"
}
}
3. To check permissions, you would use the Ory Keto API. For example, to check if a user can view the resource:
http
POST /relation-tuples/check
Content-Type: application/json
{
"namespace": "Resource",
"object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811",
"relation": "view",
"subject_id": "User:890bd6d1-fde0-40c2-aece-a0521e4199fe"
}
This model allows you to:
- Give individual users specific permissions (view and/or edit) on resources.
- Give groups of users permissions on resources.
- Easily extend to more resources by creating new relationship tuples.
You can add more resources by creating similar relationship tuples for each resource, specifying the appropriate permissions for users and groups.
For more detailed information on creating and checking permissions, you can refer to the [How to integrate permission checks to your application](https://www.ory.sh/docs/keto/guides/simple-access-check-guide) guide in the Ory documentation.
[How to integrate permission checks to your application](https://www.ory.sh/docs/keto/guides/simple-access-check-guide)chilly-king-10285
12/31/2024, 3:27 PMResource namespace: typescript import { Namespace, SubjectSet, Context } from "@ory/keto-namespace-types" class User implements Namespace {} class Group implements Namespace { related: { members: User[] } } class Resource implements Namespace { related: { viewers: (User | SubjectSet<Group, "members">)[] editors: User[] } permits = { view: (ctx: Context): boolean => this.related.viewers.includes(ctx.subject) || this.permits.edit(ctx), edit: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) } } 2. Now, let's create the necessary relationships using the HTTP API. We'll need to make several API calls: a. Give the user permission to view and edit the resource: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Resource", "object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811", "relation": "viewers", "subject_id": "User:890bd6d1-fde0-40c2-aece-a0521e4199fe" } http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Resource", "object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811", "relation": "editors", "subject_id": "User:890bd6d1-fde0-40c2-aece-a0521e4199fe" } b. Give the group members permission to view the resource: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Resource", "object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811", "relation": "viewers", "subject_set": { "namespace": "Group", "object": "13872e72-06bb-48b7-988d-d842adb18522", "relation": "members" } } 3. To check permissions, you would use the Ory Keto API. For example, to check if a user can view the resource: http POST /relation-tuples/check Content-Type: application/json { "namespace": "Resource", "object": "23a43c32-3f5b-4df7-a8d7-50147f4f1811", "relation": "view", "subject_id": "User:890bd6d1-fde0-40c2-aece-a0521e4199fe" } This model allows you to: - Give individual users specific permissions (view and/or edit) on resources. - Give groups of users permissions on resources. - Easily extend to more resources by creating new relationship tuples. You can add more resources by creating similar relationship tuples for each resource, specifying the appropriate permissions for users and groups. For more detailed information on creating and checking permissions, you can refer to the [How to integrate permission checks to your application](https://www.ory.sh/docs/keto/guides/simple-access-check-guide) guide in the Ory documentation. [How to integrate permission checks to your application](https://www.ory.sh/docs/keto/guides/simple-access-check-guide) Remember to always test your permission checks thoroughly to ensure they behave as expected in your specific use case.quick-toddler-60155
12/31/2024, 3:40 PMimport { Namespace, SubjectSet, Context } from "@ory/permission-namespace-types"
class KetoUser implements Namespace { }
class KetoGroup implements Namespace {
related: {
members: KetoUser[]
viewers: KetoGroup[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.traverse((group) => group.related.members.includes(ctx.subject))
}
}
class Resource implements Namespace {
related: {
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: KetoUser[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject)
}
}
Now, say that a Resource object might have multiple attributes with differing conditions for `view`/`edit`-permissions. For example, it might have an attribute uuid with laxer view permissions and stricter edit permissions than its other attribute name.chilly-king-10285
12/31/2024, 3:40 PMchilly-king-10285
12/31/2024, 3:40 PMResource object has multiple attributes with different view and edit permissions, we need to modify our OPL to allow for more granular control. Here's how we can adjust the model to accommodate this requirement:
typescript
import { Namespace, SubjectSet, Context } from "@ory/permission-namespace-types"
class KetoUser implements Namespace { }
class KetoGroup implements Namespace {
related: {
members: KetoUser[]
viewers: KetoGroup[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.traverse((group) => group.related.members.includes(ctx.subject))
}
}
class ResourceAttribute implements Namespace {
related: {
resource: Resource[]
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: KetoUser[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx) ||
this.related.resource.traverse((r) => r.permits.view(ctx)),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject) ||
this.related.resource.traverse((r) => r.permits.edit(ctx))
}
}
class Resource implements Namespace {
related: {
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: KetoUser[]
attributes: ResourceAttribute[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject)
}
}
In this updated OPL:
1. We've introduced a new ResourceAttribute namespace to represent individual attributes of a Resource.
2. The ResourceAttribute has its own viewers and editors relations, allowing for attribute-specific permissions.
3. The Resource namespace now has an attributes relation to link to its `ResourceAttribute`s.
4. The permission checks in ResourceAttribute cascade up to the Resource level, allowing for both attribute-specific and resource-wide permissions.
To use this model:
1. Create a Resource object:
http
PUT /admin/relation-tuples
Content-Type: application/json
{
"namespace": "Resource",
"object": "<resource_id>",
"relation": "viewers",
"subject_id": "KetoUser:<user_id>"
}
2. Create ResourceAttribute objects for uuid and `name`:
http
PUT /admin/relation-tuples
Content-Type: application/json
{
"namespace": "ResourceAttribute",
"object": "<uuid_attribute_id>",
"relation": "resource",
"subject_id": "Resource:<resource_id>"
}
http
PUT /admin/relation-tuples
Content-Type: application/json
{
"namespace": "ResourceAttribute",
"object": "<name_attribute_id>",
"relation": "resource",
"subject_id": "Resource:<resource_id>"
}
3. Set specific permissions for each attribute:
For uuid with laxer view permissions:
```http
PUT /admin/relation-tuples
Content-Type: application/jsonchilly-king-10285
12/31/2024, 3:40 PMResource object has multiple attributes with different view and edit permissions, we need to modify our OPL to allow for more granular control. Here's how we can adjust the model to accommodate this requirement: typescript import { Namespace, SubjectSet, Context } from "@ory/permission-namespace-types" class KetoUser implements Namespace { } class KetoGroup implements Namespace { related: { members: KetoUser[] viewers: KetoGroup[] } permits = { view: (ctx: Context): boolean => this.related.viewers.traverse((group) => group.related.members.includes(ctx.subject)) } } class ResourceAttribute implements Namespace { related: { resource: Resource[] viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[] editors: KetoUser[] } permits = { view: (ctx: Context): boolean => this.related.viewers.includes(ctx.subject) || this.permits.edit(ctx) || this.related.resource.traverse((r) => r.permits.view(ctx)), edit: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) || this.related.resource.traverse((r) => r.permits.edit(ctx)) } } class Resource implements Namespace { related: { viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[] editors: KetoUser[] attributes: ResourceAttribute[] } permits = { view: (ctx: Context): boolean => this.related.viewers.includes(ctx.subject) || this.permits.edit(ctx), edit: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) } } In this updated OPL: 1. We've introduced a new ResourceAttribute namespace to represent individual attributes of a Resource. 2. The ResourceAttribute has its own viewers and editors relations, allowing for attribute-specific permissions. 3. The Resource namespace now has an attributes relation to link to its `ResourceAttribute`s. 4. The permission checks in ResourceAttribute cascade up to the Resource level, allowing for both attribute-specific and resource-wide permissions. To use this model: 1. Create a Resource object: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Resource", "object": "<resource_id>", "relation": "viewers", "subject_id": "KetoUser:<user_id>" } 2. Create ResourceAttribute objects for uuid and `name`: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<uuid_attribute_id>", "relation": "resource", "subject_id": "Resource:<resource_id>" } http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<name_attribute_id>", "relation": "resource", "subject_id": "Resource:<resource_id>" } 3. Set specific permissions for each attribute: For uuid with laxer view permissions: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<uuid_attribute_id>", "relation": "viewers", "subject_set": { "namespace": "KetoGroup", "object": "<group_id>", "relation": "members" } } For name with stricter edit permissions: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<name_attribute_id>", "relation": "editors", "subject_id": "KetoUser:<admin_user_id>" } This model allows you to set different permissions for different attributes of a resource, while still maintaining the ability to set resource-wide permissions. The permission checks will consider both the attribute-specific permissions and the overall resource permissions. For more information on creating complex permission models, you can refer to the [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model) guide in the Ory documentation. [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model) Remember to thoroughly test your permission checks to ensure they behave as expected in your specific use case.quick-toddler-60155
12/31/2024, 3:46 PMchilly-king-10285
12/31/2024, 3:46 PMchilly-king-10285
12/31/2024, 3:46 PMview permission in the ResourceAttribute namespace checks: - If the subject is in the attribute's viewers list - If the subject has edit permission on the attribute - If the subject has view permission on the parent Resource This means that if a user has view permission on the Resource but not on the specific ResourceAttribute, they would still be able to view the attribute. However, if stricter permissions are set on the ResourceAttribute, those would be enforced. 2. For editing a ResourceAttribute: The edit permission in the ResourceAttribute namespace checks: - If the subject is in the attribute's editors list - If the subject has edit permission on the parent Resource This means that having edit permission on the Resource grants edit access to all attributes, unless specifically restricted at the attribute level. In essence, the model implements a "most permissive" approach where: - ResourceAttribute permissions can grant additional access beyond what's set on the Resource. - ResourceAttribute permissions can restrict access that would otherwise be granted by the Resource permissions. This allows for fine-grained control at the attribute level while still maintaining the ability to set broader permissions at the Resource level. For example, if a user has edit permission on the Resource but is explicitly excluded from editing a specific ResourceAttribute, they would not be able to edit that attribute despite having general edit permissions on the Resource. It's important to note that this behavior is defined by the OPL we've written, specifically in the permits functions of the ResourceAttribute namespace. You can adjust these functions if you want to change how permissions are prioritized or combined. For more detailed information on how to fine-tune permission models in Ory Keto, you can refer to the [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model) guide in the Ory documentation.quick-toddler-60155
12/31/2024, 3:48 PMResourceAttribute permissions can restrict access that would otherwise be granted by the Resource permissions.
chilly-king-10285
12/31/2024, 3:48 PMchilly-king-10285
12/31/2024, 3:48 PMtypescript
import { Namespace, SubjectSet, Context } from "@ory/permission-namespace-types"
class KetoUser implements Namespace { }
class KetoGroup implements Namespace {
related: {
members: KetoUser[]
}
}
class ResourceAttribute implements Namespace {
related: {
resource: Resource[]
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: KetoUser[]
restricted_viewers: KetoUser[]
restricted_editors: KetoUser[]
}
permits = {
view: (ctx: Context): boolean =>
!this.related.restricted_viewers.includes(ctx.subject) &&
(this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx) ||
this.related.resource.traverse((r) => r.permits.view(ctx))),
edit: (ctx: Context): boolean =>
!this.related.restricted_editors.includes(ctx.subject) &&
(this.related.editors.includes(ctx.subject) ||
this.related.resource.traverse((r) => r.permits.edit(ctx)))
}
}
class Resource implements Namespace {
related: {
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: KetoUser[]
attributes: ResourceAttribute[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject)
}
}
In this updated model:
1. We've added restricted_viewers and restricted_editors relations to the ResourceAttribute namespace.
2. The permits functions in ResourceAttribute now first check if the subject is in the restricted list before checking for permissions.
To implement this restriction, you would create relationship tuples using the Ory Keto API. Here's an example of how to restrict a user from viewing a specific attribute:
http
PUT /admin/relation-tuples
Content-Type: application/json
{
"namespace": "ResourceAttribute",
"object": "<attribute_id>",
"relation": "restricted_viewers",
"subject_id": "KetoUser:<user_id>"
}
This setup ensures that even if a user has view permissions on the Resource level, they won't be able to view a specific attribute if they're in its restricted_viewers list.
The same principle applies to edit permissions with the restricted_editors relation.
This approach allows you to:
1. Set broad permissions at the Resource level.
2. Refine permissions for specific attributes.
3. Explicitly deny access to certain attributes for specific users, overriding broader permissions.chilly-king-10285
12/31/2024, 3:48 PMtypescript import { Namespace, SubjectSet, Context } from "@ory/permission-namespace-types" class KetoUser implements Namespace { } class KetoGroup implements Namespace { related: { members: KetoUser[] } } class ResourceAttribute implements Namespace { related: { resource: Resource[] viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[] editors: KetoUser[] restricted_viewers: KetoUser[] restricted_editors: KetoUser[] } permits = { view: (ctx: Context): boolean => !this.related.restricted_viewers.includes(ctx.subject) && (this.related.viewers.includes(ctx.subject) || this.permits.edit(ctx) || this.related.resource.traverse((r) => r.permits.view(ctx))), edit: (ctx: Context): boolean => !this.related.restricted_editors.includes(ctx.subject) && (this.related.editors.includes(ctx.subject) || this.related.resource.traverse((r) => r.permits.edit(ctx))) } } class Resource implements Namespace { related: { viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[] editors: KetoUser[] attributes: ResourceAttribute[] } permits = { view: (ctx: Context): boolean => this.related.viewers.includes(ctx.subject) || this.permits.edit(ctx), edit: (ctx: Context): boolean => this.related.editors.includes(ctx.subject) } } In this updated model: 1. We've added restricted_viewers and restricted_editors relations to the ResourceAttribute namespace. 2. The permits functions in ResourceAttribute now first check if the subject is in the restricted list before checking for permissions. To implement this restriction, you would create relationship tuples using the Ory Keto API. Here's an example of how to restrict a user from viewing a specific attribute: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<attribute_id>", "relation": "restricted_viewers", "subject_id": "KetoUser:<user_id>" } This setup ensures that even if a user has view permissions on the Resource level, they won't be able to view a specific attribute if they're in its restricted_viewers list. The same principle applies to edit permissions with the restricted_editors relation. This approach allows you to: 1. Set broad permissions at the Resource level. 2. Refine permissions for specific attributes. 3. Explicitly deny access to certain attributes for specific users, overriding broader permissions. Remember to thoroughly test your permission checks to ensure they behave as expected in your specific use case. For more information on creating complex permission models in Ory Keto, you can refer to the [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model) guide in the Ory documentation. [Create a permission model](https://www.ory.sh/docs/keto/modeling/create-permission-model)quick-toddler-60155
12/31/2024, 4:02 PMtypescript
import { Namespace, SubjectSet, Context } from "@ory/permission-namespace-types"
class KetoUser implements Namespace { }
class KetoGroup implements Namespace {
related: {
members: KetoUser[]
viewers: KetoGroup[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.traverse((group) => group.related.members.includes(ctx.subject))
}
}
class ResourceAttribute implements Namespace {
related: {
resource: Resource[]
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: KetoUser[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx) ||
this.related.resource.traverse((r) => r.permits.view(ctx)),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject) ||
this.related.resource.traverse((r) => r.permits.edit(ctx))
}
}
class Resource implements Namespace {
related: {
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: KetoUser[]
attributes: ResourceAttribute[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject)
}
}
When creating a ResourceAttribute I would create a relation resource in its namespace - would I also need to create the inverse relation attributes in the Resource namespace?chilly-king-10285
12/31/2024, 4:02 PMchilly-king-10285
12/31/2024, 4:02 PMattributes in the Resource namespace when creating a ResourceAttribute. Here's why: 1. In Ory Keto, relationships are unidirectional by default. The system doesn't automatically create or manage bidirectional relationships. 2. The Resource namespace has an attributes relation defined in its related section, but this is used in the OPL to define the structure and permissions, not to create actual relationship tuples. 3. When you create a ResourceAttribute and set its resource relation, that's sufficient for the permission checks to work as defined in your OPL. To create a ResourceAttribute and associate it with a Resource, you would only need to create a relationship tuple like this: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<attribute_id>", "relation": "resource", "subject_id": "Resource:<resource_id>" } This relationship is enough for the permission checks to traverse from the ResourceAttribute to its associated Resource when needed, as defined in your permits functions. However, it's important to note that if you need to query from the Resource to find its ResourceAttributes, you might face some limitations with this unidirectional approach. If such queries are important for your application logic (outside of permission checks), you might want to consider creating the inverse relationship as well, even though it's not strictly necessary for the permission model to function. For more information on relationship management in Ory Keto, you can refer to the [Relationships](https://www.ory.sh/docs/keto/concepts/relation-tuples) concept in the Ory documentation. [Relationships](https://www.ory.sh/docs/keto/concepts/relation-tuples) Remember, the most efficient approach depends on your specific use case and query patterns. Always test your implementation thoroughly to ensure it meets your application's requirements.quick-toddler-60155
12/31/2024, 4:05 PMuuid belonging to a specific resource 3db0efed-32bf-4e3e-a8ba-81da1257c997 for a user 266a92b0-15fd-4776-823f-4e1449591fcc. Can you translate that permission check into HTTP API calls to a Keto service with the provided OPL model?chilly-king-10285
12/31/2024, 4:05 PMchilly-king-10285
12/31/2024, 4:06 PMuuid belonging to a specific resource for a given user using the Ory Keto HTTP API, we need to make a series of API calls based on the OPL model you've provided. Here's how we can translate this permission check into HTTP API calls:
1. First, we need to check the permission for viewing the specific ResourceAttribute:
http
POST /relation-tuples/check
Content-Type: application/json
{
"namespace": "ResourceAttribute",
"object": "<uuid_attribute_id>",
"relation": "view",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
}
Replace <uuid_attribute_id> with the actual ID of the uuid attribute.
2. If the first check returns false, we need to check if the user has permission to view the Resource itself:
http
POST /relation-tuples/check
Content-Type: application/json
{
"namespace": "Resource",
"object": "3db0efed-32bf-4e3e-a8ba-81da1257c997",
"relation": "view",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
}
3. If both checks return false, we need to check if the user has edit permissions on either the ResourceAttribute or the Resource:
For the ResourceAttribute:
http
POST /relation-tuples/check
Content-Type: application/json
{
"namespace": "ResourceAttribute",
"object": "<uuid_attribute_id>",
"relation": "edit",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
}
For the Resource:
http
POST /relation-tuples/check
Content-Type: application/json
{
"namespace": "Resource",
"object": "3db0efed-32bf-4e3e-a8ba-81da1257c997",
"relation": "edit",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
}
The user has permission to view the attribute if any of these checks return true.
Alternatively, you can use the batch check endpoint to perform all these checks in a single API call:
http
POST /relation-tuples/check/bulk
Content-Type: application/json
{
"tuples": [
{
"namespace": "ResourceAttribute",
"object": "<uuid_attribute_id>",
"relation": "view",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
},
{
"namespace": "Resource",
"object": "3db0efed-32bf-4e3e-a8ba-81da1257c997",
"relation": "view",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
},
{
"namespace": "ResourceAttribute",
"object": "<uuid_attribute_id>",
"relation": "edit",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
},
{
"namespace": "Resource",
"object": "3db0efed-32bf-4e3e-a8ba-81da1257c997",
"relation": "edit",
"subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc"
}
]
}
This batch check will return the results for all the permission checks in a single response. The user has permission to view the attribute if any of these checks return true.chilly-king-10285
12/31/2024, 4:06 PMuuid belonging to a specific resource for a given user using the Ory Keto HTTP API, we need to make a series of API calls based on the OPL model you've provided. Here's how we can translate this permission check into HTTP API calls: 1. First, we need to check the permission for viewing the specific ResourceAttribute: http POST /relation-tuples/check Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<uuid_attribute_id>", "relation": "view", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" } Replace <uuid_attribute_id> with the actual ID of the uuid attribute. 2. If the first check returns false, we need to check if the user has permission to view the Resource itself: http POST /relation-tuples/check Content-Type: application/json { "namespace": "Resource", "object": "3db0efed-32bf-4e3e-a8ba-81da1257c997", "relation": "view", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" } 3. If both checks return false, we need to check if the user has edit permissions on either the ResourceAttribute or the Resource: For the ResourceAttribute: http POST /relation-tuples/check Content-Type: application/json { "namespace": "ResourceAttribute", "object": "<uuid_attribute_id>", "relation": "edit", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" } For the Resource: http POST /relation-tuples/check Content-Type: application/json { "namespace": "Resource", "object": "3db0efed-32bf-4e3e-a8ba-81da1257c997", "relation": "edit", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" } The user has permission to view the attribute if any of these checks return true. Alternatively, you can use the batch check endpoint to perform all these checks in a single API call: http POST /relation-tuples/check/bulk Content-Type: application/json { "tuples": [ { "namespace": "ResourceAttribute", "object": "<uuid_attribute_id>", "relation": "view", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" }, { "namespace": "Resource", "object": "3db0efed-32bf-4e3e-a8ba-81da1257c997", "relation": "view", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" }, { "namespace": "ResourceAttribute", "object": "<uuid_attribute_id>", "relation": "edit", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" }, { "namespace": "Resource", "object": "3db0efed-32bf-4e3e-a8ba-81da1257c997", "relation": "edit", "subject_id": "KetoUser:266a92b0-15fd-4776-823f-4e1449591fcc" } ] } This batch check will return the results for all the permission checks in a single response. The user has permission to view the attribute if any of these checks return true. For more details on using the Ory Keto API for permission checks, you can refer to the [Ory Keto API Reference](https://www.ory.sh/docs/keto/reference/rest-api). Remember to replace <uuid_attribute_id> with the actual ID of the uuid attribute in your system. Also, ensure that your Keto service is properly configured and accessible at the correct endpoint. [Ory Keto API Reference](https://www.ory.sh/docs/keto/reference/rest-api)quick-toddler-60155
12/31/2024, 4:12 PMimport { Namespace, SubjectSet, Context } from "@ory/permission-namespace-types"
class KetoUser implements Namespace { }
class KetoGroup implements Namespace {
related: {
members: KetoUser[]
}
}
class ResourceAttribute implements Namespace {
related: {
resource: Resource[]
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: (KetoUser | SubjectSet<KetoGroup, "members">)[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx) ||
this.related.resource.traverse((r) => r.permits.view(ctx)),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject) ||
this.related.resource.traverse((r) => r.permits.edit(ctx))
}
}
class Resource implements Namespace {
related: {
viewers: (KetoUser | SubjectSet<KetoGroup, "members">)[]
editors: (KetoUser | SubjectSet<KetoGroup, "members">)[]
attributes: ResourceAttribute[]
}
permits = {
view: (ctx: Context): boolean =>
this.related.viewers.includes(ctx.subject) ||
this.permits.edit(ctx),
edit: (ctx: Context): boolean =>
this.related.editors.includes(ctx.subject)
}
}
Now imagine that we create the following in Keto:
[
{
"namespace": "KetoUser",
"subject_id": "df1eaebb-8dfb-4495-ab8d-c96feaa9f99b"
},
{
"namespace": "KetoUser",
"subject_id": "00822a61-5ab1-491f-bbaf-da37913adb6a"
},
{
"namespace": "KetoGroup",
"subject_id": "parents_of_df1eaebb-8dfb-4495-ab8d-c96feaa9f99b"
},
{
"namespace": "KetoGroup",
"subject_id": "00822a61-5ab1-491f-bbaf-da37913adb6a",
"relation": "members",
"object": "parents_of_df1eaebb-8dfb-4495-ab8d-c96feaa9f99b"
},
{
"namespace": "Resource",
"subject_id": "3db0efed-32bf-4e3e-a8ba-81da1257c997"
},
{
"namespace": "Resource",
"subject_set": {
"namespace": "KetoGroup",
"relation": "members",
"object": "parents_of_df1eaebb-8dfb-4495-ab8d-c96feaa9f99b"
},
"relation": "viewers",
"object": "3db0efed-32bf-4e3e-a8ba-81da1257c997"
},
{
"namespace": "ResourceAttribute",
"subject_id": "uuid"
},
{
"namespace": "ResourceAttribute",
"subject_id": "uuid",
"relation": "resource",
"object": "3db0efed-32bf-4e3e-a8ba-81da1257c997"
}
]
Would this make the permission check with the body below return True?
{
"namespace": "ResourceAttribute",
"subject_id": "00822a61-5ab1-491f-bbaf-da37913adb6a",
"relation": "view",
"object": "uuid"
}chilly-king-10285
12/31/2024, 4:12 PMchilly-king-10285
12/31/2024, 4:13 PMview permit, it checks three conditions: a. If the subject is in the attribute's viewers list (not set in this case) b. If the subject has edit permission on the attribute (not set in this case) c. If the subject has view permission on the parent Resource 3. Since the first two conditions are not met, we need to check if the user has view permission on the parent Resource "3db0efed-32bf-4e3e-a8ba-81da1257c997". 4. In the Resource's view permit, it checks: a. If the subject is in the resource's viewers list b. If the subject has edit permission on the resource 5. Looking at the relationships you've created: - The user "00822a61-5ab1-491f-bbaf-da37913adb6a" is a member of the group "parents_of_df1eaebb-8dfb-4495-ab8d-c96feaa9f99b" - The group "parents_of_df1eaebb-8dfb-4495-ab8d-c96feaa9f99b" has viewing permission on the Resource "3db0efed-32bf-4e3e-a8ba-81da1257c997" Given this analysis, the permission check would indeed return True. Here's why: 1. The user is a member of a group that has viewing permission on the parent Resource. 2. This satisfies the condition in the Resource's view permit. 3. Which in turn satisfies the third condition in the ResourceAttribute's view permit. Therefore, the permission check with the body you provided would return True, allowing the user "00822a61-5ab1-491f-bbaf-da37913adb6a" to view the "uuid" ResourceAttribute. It's worth noting that this permission is granted indirectly through group membership and resource-level permissions, rather than direct permissions on the ResourceAttribute itself. This demonstrates the power and flexibility of the permission model you've set up, allowing for hierarchical and group-based access control.quick-toddler-60155
12/31/2024, 4:15 PMResource objects 1b291885-9aa2-41a4-899f-67ee5e322ee9 and 4ab8f34f-608c-4bd6-84de-d230a30ede38 . Both of them should have a uuid attribute that permissions can be checked on.
How could I accomplish this simply?chilly-king-10285
12/31/2024, 4:15 PMchilly-king-10285
12/31/2024, 4:16 PMResource objects, each with a uuid attribute that permissions can be checked on, you can use the existing OPL model and create the necessary relationships in Ory Keto. Here's how you can do this simply: 1. First, create the two Resource objects: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Resource", "object": "1b291885-9aa2-41a4-899f-67ee5e322ee9" } http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "Resource", "object": "4ab8f34f-608c-4bd6-84de-d230a30ede38" } 2. Then, create a uuid ResourceAttribute for each Resource: http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "uuid_1b291885-9aa2-41a4-899f-67ee5e322ee9", "relation": "resource", "subject_id": "Resource:1b291885-9aa2-41a4-899f-67ee5e322ee9" } http PUT /admin/relation-tuples Content-Type: application/json { "namespace": "ResourceAttribute", "object": "uuid_4ab8f34f-608c-4bd6-84de-d230a30ede38", "relation": "resource", "subject_id": "Resource:4ab8f34f-608c-4bd6-84de-d230a30ede38" } In these examples, we're using uuid_<resource_id> as the object identifier for the ResourceAttribute to ensure uniqueness. You could use any unique identifier that makes sense for your application. Now, to check permissions on these uuid attributes, you would use: For the first Resource: http POST /relation-tuples/check Content-Type: application/json { "namespace": "ResourceAttribute", "object": "uuid_1b291885-9aa2-41a4-899f-67ee5e322ee9", "relation": "view", "subject_id": "KetoUser:<user_id>" } For the second Resource: http POST /relation-tuples/check Content-Type: application/json { "namespace": "ResourceAttribute", "object": "uuid_4ab8f34f-608c-4bd6-84de-d230a30ede38", "relation": "view", "subject_id": "KetoUser:<user_id>" } This approach allows you to: 1. Create multiple Resource objects 2. Associate a uuid attribute with each Resource 3. Check permissions on the uuid attribute for each Resource independently The permissions will be evaluated based on the OPL model you provided earlier, considering both the ResourceAttribute permissions and the permissions of the parent Resource. For more information on managing relationships and checking permissions in Ory Keto, you can refer to the [Ory Keto API Reference](https://www.ory.sh/docs/keto/reference/rest-api). [Ory Keto API Reference](https://www.ory.sh/docs/keto/reference/rest-api) Remember to replace <user_id> with the actual ID of the user you're checking permissions for.quick-toddler-60155
12/31/2024, 4:19 PMsubject_id fields. Is this standard practice? For example you wrote "subject_id": "KetoUser:<user_id>".chilly-king-10285
12/31/2024, 4:19 PMchilly-king-10285
12/31/2024, 4:19 PMsubject_id fields. This is not standard practice in Ory Keto, and I apologize for the confusion. Let me clarify the correct way to specify subjects in Ory Keto: 1. When referring to a subject in a relation tuple, you should use only the identifier of the subject, without the namespace prefix. 2. The namespace is already specified in the namespace field of the relation tuple, so it doesn't need to be repeated in the subject_id. Here's the correct way to specify a subject: json { "namespace": "KetoUser", "object": "<object_id>", "relation": "some_relation", "subject_id": "<user_id>" } Not: json { "namespace": "KetoUser", "object": "<object_id>", "relation": "some_relation", "subject_id": "KetoUser:<user_id>" // This is incorrect } Thank you for bringing this to my attention. It's important to use the correct syntax when working with Ory Keto to ensure proper functioning of your permission system. For more detailed information on the correct syntax for relation tuples in Ory Keto, you can refer to the [Relationships](https://www.ory.sh/docs/keto/concepts/relation-tuples) concept in the Ory documentation. [Relationships](https://www.ory.sh/docs/keto/concepts/relation-tuples)quick-toddler-60155
12/31/2024, 4:20 PMnamespace apply to both object and subject_id?chilly-king-10285
12/31/2024, 4:20 PMchilly-king-10285
12/31/2024, 4:20 PMnamespace in an Ory Keto relation tuple applies to the object, but not to the subject_id. Let me explain this in more detail: 1. For the `object`: The namespace field specifies the namespace of the object in the relation tuple. This means that the object identifier is understood to be within the context of the specified namespace. 2. For the `subject_id`: The subject_id is treated differently. It does not inherit the namespace specified in the relation tuple. Instead, when you use a subject_id, Ory Keto assumes it belongs to its own namespace, which should be specified elsewhere when that subject is defined. This distinction is important because it allows for cross-namespace relationships. For example, you can have a user (from a User namespace) having permissions on a resource (from a Resource namespace). Here's an example to illustrate this: json { "namespace": "Resource", "object": "document123", "relation": "viewer", "subject_id": "alice" } In this relation tuple: - The object "document123" is in the "Resource" namespace. - The subject_id "alice" is not in the "Resource" namespace. It's assumed to be in its own namespace (e.g., User), which would be defined separately. This design allows for flexible and powerful permission modeling across different types of entities in your system. For more detailed information on how namespaces work in Ory Keto, you can refer to the [Namespaces](https://www.ory.sh/docs/keto/concepts/namespaces) concept in the Ory documentation. [Namespaces](https://www.ory.sh/docs/keto/concepts/namespaces)