Skip to content

Latest commit

 

History

History
340 lines (279 loc) · 12.4 KB

object-to-object-relationships.mdx

File metadata and controls

340 lines (279 loc) · 12.4 KB
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';

Object to Object Relationships

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 contain channels
  • channels can contain posts
  • channels can contain threads
  • threads can contain posts
  • bookshelf can have books
  • trips can have bookings
  • account can contain transactions
  • buildings can have doors

Before You Start

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.

<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:

Modeling User Groups

You need to know how to add users to groups and grant groups access to resources. Learn more →

Concepts

  • 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

Step By Step

01. Create Parent Relations In Document

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'}] }, }, }, }, ], }} />

02. Add Parent Relationship Tuples

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', }, ]} />

03. Check That Parent Folders Have Permissions

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. :::

Advanced Object to Object Relationships

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.

01. Create Authorization Model With Object To Object Relationships

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.

02. Adding Relationship Tuples

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', }, ]} />

03. Check To See If Access Is Allowed Without Direct Relationship

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.

04. Disassociating Plan From Feature

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} />

Related Sections

<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', }, ]} />