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?brief-egg-61061
12/28/2024, 9:34 PMbrief-egg-61061
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 PMbrief-egg-61061
12/28/2024, 9:51 PMbrief-egg-61061
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.brief-egg-61061
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 theandview
permissions on theedit
namespace.Attribute
brief-egg-61061
12/28/2024, 10:17 PMbrief-egg-61061
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 PMbrief-egg-61061
12/28/2024, 10:30 PMbrief-egg-61061
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>
brief-egg-61061
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 AMbrief-egg-61061
12/31/2024, 2:27 AMbrief-egg-61061
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.brief-egg-61061
12/31/2024, 2:30 AMbrief-egg-61061
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)
brief-egg-61061
12/31/2024, 2:36 AMbrief-egg-61061
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
.brief-egg-61061
12/31/2024, 3:12 PMbrief-egg-61061
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
.brief-egg-61061
12/31/2024, 3:27 PMbrief-egg-61061
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)brief-egg-61061
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
.brief-egg-61061
12/31/2024, 3:40 PMbrief-egg-61061
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/jsonbrief-egg-61061
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 PMbrief-egg-61061
12/31/2024, 3:46 PMbrief-egg-61061
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.
brief-egg-61061
12/31/2024, 3:48 PMbrief-egg-61061
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.brief-egg-61061
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?brief-egg-61061
12/31/2024, 4:02 PMbrief-egg-61061
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?brief-egg-61061
12/31/2024, 4:05 PMbrief-egg-61061
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
.brief-egg-61061
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"
}
brief-egg-61061
12/31/2024, 4:12 PMbrief-egg-61061
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?brief-egg-61061
12/31/2024, 4:15 PMbrief-egg-61061
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>"
.brief-egg-61061
12/31/2024, 4:19 PMbrief-egg-61061
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
?brief-egg-61061
12/31/2024, 4:20 PMbrief-egg-61061
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)