sidebar_position | description | slug |
---|---|---|
4 |
Modeling Concepts: Concentric Relationships |
/modeling/building-blocks/concentric-relationships |
import { AuthzModelSnippetViewer, CardBox, CheckRequestViewer, DocumentationNotice, Playground, ProductConcept, ProductName, ProductNameFormat, RelatedSection, RelationshipTuplesViewer, } from '@components/Docs';
In this short guide, you'll learn how to represent a concentric .
For example, if you want to have all editors of a document also be viewers of said document.
Concentric relations make the most sense when your domain logic has nested relations, where one having relation implies having another relation.
For example:
- all
editors
areviewers
- all
managers
aremembers
- all
device_managers
aredevice_renamers
This allows you to only create a single relationship tuple rather than creating n relationship tuples for each relation.
To better understand this guide, you should be 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 viewer
.
Let us also assume that we have a document
called "meeting_notes.doc" and bob is assigned as editor to this document.
document
that can have users as editor
and viewer
.document
called "meeting_notes.doc" and bob is assigned as editor to this document.<AuthzModelSnippetViewer configuration={{ schema_version: '1.1', type_definitions: [ { type: 'user', }, { type: 'document', relations: { viewer: { this: {}, }, editor: { this: {}, }, }, metadata: { relations: { viewer: { directly_related_user_types: [{type: 'user'}] }, editor: { directly_related_user_types: [{type: 'user'}] }, }, }, }, ], }} />
The current state of the system is represented by the following relationship tuples being in the system already:
<RelationshipTuplesViewer relationshipTuples={[ { user: 'user:bob', relation: 'editor', object: 'document:meeting_notes.doc', }, ]} />
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
With the current type definition, there isn't a way to indicate that all editors
of a certain document
are also automatically viewers
of that document. So for a certain user, in order to indicate that they can both edit
and view
a certain document
, two need to be created (one for editor
, and another for viewer
).
Instead of creating two relationship tuples, we can leverage concentric relationships by defining editors are viewers.
Our authorization model becomes the following:
<AuthzModelSnippetViewer
configuration={{
schema_version: '1.1',
type_definitions: [
{
type: 'user',
},
{
type: 'document',
relations: {
viewer: {
union: {
child: [
{
// a user can be assigned a direct viewer
relation, i.e., not implied through another relation
this: {},
},
{
// a user that is an editor is also implicitly a viewer
computedUserset: {
relation: 'editor',
},
},
],
},
},
editor: {
this: {},
},
},
metadata: {
relations: {
viewer: { directly_related_user_types: [{type: 'user'}] },
editor: { directly_related_user_types: [{type: 'user'}] },
},
},
},
],
}}
/>
:::info
viewer
of a document
are any of:
- users that are directly assigned as
viewer
- users that have
editor
of the document
:::
With this authorization model change, having an editor
relationship with a certain document implies having a viewer
relationship with that same document.
Since we had a relationship tuple that indicates that bob is an editor
of document:meeting_notes.doc, this means bob is now implicitly a viewer
of document:meeting_notes.doc.
If we now check: is bob a viewer of document:meeting_notes.doc? we would get the following:
<CheckRequestViewer user={'user:bob'} relation={'viewer'} object={'document:meeting_notes.doc'} allowed={true} />
:::caution Note When creating relationship tuples for make sure to use unique ids for each object and user within your application domain. We're using first names and simple ids to just illustrate an easy-to-follow example. :::
<RelatedSection description="Check the following sections for more on how concentric relationships can be used." relatedLinks={[ { title: 'Modeling Google Drive', description: 'See how to indicate that editors are commenters and viewers in Google Drive.', link: '../advanced/gdrive#01-individual-permissions', id: '../advanced/gdrive.mdx#01-individual-permissions', }, { title: 'Modeling GitHub', description: 'See how to indicate that repository admins are writers and readers in GitHub.', link: '../advanced/github#01-permissions-for-individuals-in-an-org', id: '../advanced/github.mdx#01-permissions-for-individuals-in-an-org', }, ]} />