sidebar_position | slug | description |
---|---|---|
2 |
/modeling/building-blocks/object-to-object-relationships |
Modeling relationships between objects (e.g. folder parent of a document) |
import { AuthzModelSnippetViewer, CardBox, CheckRequestViewer, DocumentationNotice, Playground, ProductConcept, ProductName, ProductNameFormat, RelatedSection, WriteRequestViewer, } from '@components/Docs';
In this guide you'll learn how to model your application with that are not specifically tied to a user. For example, a folder
is a parent
of a document
.
This design pattern is helpful in the case where there are relationships between different objects. With , so long as both objects are in a type defined in the , relationship tuples can be added to indicate a relationship between them.
For example:
communities
can containchannels
channels
can containposts
channels
can containthreads
threads
can containposts
bookshelf
can havebooks
trips
can havebookings
account
can containtransactions
buildings
can havedoors
To better follow this guide, make sure you're familiar with some and know how to develop the things listed below.
You will start with the below, it represents a document
that can have users as editor
, and folder
type that can have users related as viewer
.
document
that can have users as editor
, and folder
type that can have users related as viewer
.<AuthzModelSnippetViewer configuration={{ schema_version: '1.1', type_definitions: [ { type: 'user', }, { type: 'document', relations: { editor: { this: {}, }, }, metadata: { relations: { editor: { directly_related_user_types: [{type: 'user'}] }, }, }, }, { type: 'folder', relations: { viewer: { this: {}, }, }, metadata: { relations: { viewer: { directly_related_user_types: [{type: 'user'}] }, }, }, }, ], }} />
In addition, you will need to know the following:
You need to know how to add users to groups and grant groups access to resources. Learn more →
- A : a class of objects that have similar characteristics
- A : an entity in the system that can be related to an object
- A : is a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
- An : represents an entity in the system. Users' relationships to it can be define through relationship tuples and the authorization model
- A : a grouping consisting of a user, a relation and an object stored in
To represent that a folder
can be a parent
of a document
, we first need to modify our document
to allow a parent
.
<AuthzModelSnippetViewer configuration={{ schema_version: '1.1', type_definitions: [ { type: 'user', }, { type: 'document', relations: { // allow others to have 'parent' relation to 'document' parent: { this: {}, }, editor: { this: {}, }, }, metadata: { relations: { parent: { directly_related_user_types: [{type: 'folder'}] }, editor: { directly_related_user_types: [{type: 'user'}] }, }, }, }, { type: 'folder', relations: { viewer: { this: {}, }, }, metadata: { relations: { viewer: { directly_related_user_types: [{type: 'user'}] }, }, }, }, ], }} />
Once the type definition is updated, we can now create the between a folder
as a parent
of a document
. To do this, we will create a new that describes: folder:budgets is a parent
of document:may_budget.doc. In , in the relationship tuples can not only be IDs, but also other objects in the form of type:object_id
.
<WriteRequestViewer
relationshipTuples={[
{
_description: 'The user in this case is another object where the type is folder
and the object_id is budgets
',
user: 'folder:budgets',
relation: 'parent',
object: 'document:may_budget.doc',
},
]}
/>
Once that relationship tuple is added to , we can if the relationship is valid by asking the following: "is folder:budgets a parent of document:may_budget.doc?"
<CheckRequestViewer user={'folder:budgets'} relation={'parent'} object={'document:may_budget.doc'} allowed={true} />
It is important to note that the current authorization model does not imply inheritance of permissions. Even though folder:budgets is a parent
of document:may_budget.doc, it does not inherit the editor
relation from parent
to document
. Meaning editors
on folder:budgets are not editors
on document:may_budget.doc. Further configuration changes are needed to indicate that and will be tackled in a later guide.
:::caution Note: When creating relationship tuples for make sure to use unique ids for each object and user within your application domain. We are using first names and simple ids to just illustrate an easy-to-follow example. :::
Object to object can be used for more advanced use case, such as entitlements. An example use case is to allow subscribers to be entitled to different plans.
To do this, the authorization model will have two - feature and plan.
<AuthzModelSnippetViewer configuration={{ schema_version: '1.1', type_definitions: [ { type: 'user', }, { type: 'feature', relations: { associated_plan: { this: {}, }, access: { union: { child: [ { this: {}, }, { tupleToUserset: { tupleset: { relation: 'associated_plan', }, computedUserset: { relation: 'subscriber_member', }, }, }, ], }, }, }, metadata: { relations: { associated_plan: { directly_related_user_types: [{type: 'plan'}] }, access: { directly_related_user_types: [{type: 'user'}] }, }, }, }, { type: 'plan', relations: { subscriber_member: { this: {}, }, }, metadata: { relations: { subscriber_member: { directly_related_user_types: [{type: 'user'}] }, }, }, }, ], }} />
Type feature
has two relations, associated_plan and access. Relation associated_plan
allows associating plans with features while access
defines who can access the feature. In our case, the access can be achieved either from
- via direct relationship type restrictions.
or
this
- object to object relationship where a user can access because it is a subscriber_member of a particular plan AND that plan is associated with the feature.
Here, we define plan
as the user of object feature
with relationship associated_plan
rather than defining feature
as the user of object plan
with relationship feature
. The reason we choose the former is that we want to describe our system in the following plain language:
- A user can access a feature in a plan if they are a subscriber member of a plan that is the associated plan of a feature.
This will give us a flow of user->organization->plan->feature and allows us to answer the question of whether user can access a feature rather than whether user is subscriber of a plan.
To realize the relationship, we will need to add the following relationship tuples.
<WriteRequestViewer relationshipTuples={[ { _description: 'make anne as subscriber_member for plan:advanced', user: 'user:anne', relation: 'subscriber_member', object: 'plan:advanced', }, { _description: 'The advanced plan is associated with the data preview feature', user: 'plan:advanced', relation: 'associated_plan', object: 'feature:data_preview', }, ]} />
To validate that the authorization model and relationship tuples are correct, we can ask the question:
<CheckRequestViewer user={'user:anne'} relation={'access'} object={'feature:data_preview'} allowed={true} />
We see that anne
is allowed to access
feature:data_preview
without requiring direct relationship.
At any point in time, plan:advanced
may be disassociated from feature:data_preview
.
<WriteRequestViewer deleteRelationshipTuples={[ { _description: 'Remove advanced plan from data preview feature', user: 'plan:advanced', relation: 'associated_plan', object: 'feature:data_preview', }, ]} />
When this is the case, anne
will no longer have access
to feature:data_preview
even though she is still a subscriber_member
of plan:advanced
.
<CheckRequestViewer user={'user:anne'} relation={'access'} object={'feature:data_preview'} allowed={false} /> <CheckRequestViewer user={'user:anne'} relation={'subscriber_member'} object={'plan:advanced'} allowed={true} />
<RelatedSection description="Check the following sections for more on how object-to-object relationships can be used." relatedLinks={[ { title: 'Advanced Modeling Patterns: Entitlements', description: 'Learn how to model entitlement access patterns.', link: '../advanced/entitlements', id: '../advanced/entitlements.mdx', }, { title: 'Modeling Parent-Child Relationships', description: 'Learn how to model parent and child relationships.', link: '../parent-child', id: '../parent-child.mdx', }, { title: 'Modeling User Groups', description: 'Learn how to model user groups.', link: '../user-groups', id: '../user-groups.mdx', }, ]} />