sidebar_position | slug | description |
---|---|---|
2 |
/interacting/managing-relationships-between-objects |
Granting a user access to a particular object through a relationship with another object |
import { AuthzModelSnippetViewer, CardBox, CheckRequestViewer, DocumentationNotice, ProductConcept, ProductName, ProductNameFormat, RelatedSection, WriteRequestViewer, } from '@components/Docs';
In this guide you will learn how to grant a user access to a particular object through a relationship with another object.
Giving user access through a relationship with another object is helpful because it allows scaling as the number of object grows. For example:
- organization that owns many repos
- team that administers many documents
In order to understand this guide correctly you must be familiar with some and know how to develop the things that we will list below.
Assume that you have the following
- a
repo
type that can have a admin
relation
repo
type that can have a admin
relation<AuthzModelSnippetViewer configuration={{ schema_version: '1.1', type_definitions: [ { type: 'user', }, { type: 'repo', relations: { admin: { this: {}, }, }, metadata: { relations: { admin: { directly_related_user_types: [{ type: 'user' }] }, }, }, }, ], }} />
In addition, you will need to know the following:
You need to know how to create an authorization model and create a relationship tuple to grant a user access to an object. 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
For the current model, a can be related as an admin
to an of repo
. If we wanted to have Anne be related to two repos, repo:1
and repo:2
, we would have to add two , like so:
<WriteRequestViewer relationshipTuples={[ { user: 'user:anne', relation: 'admin', object: 'repo:1', }, { user: 'user:anne', relation: 'admin', object: 'repo:2', }, ]} />
In general, every time we wanted to add a new admin
relationship to a repo
we'd have to add a new tuple. This doesn't scale as the list of repo
s and users grows.
Another way of modeling this is to have an authorization model as follows:
<AuthzModelSnippetViewer configuration={{ schema_version: '1.1', type_definitions: [ { type: 'user', }, { type: 'repo', relations: { admin: { union: { child: [ { this: {}, }, { tupleToUserset: { tupleset: { object: '', relation: 'owner', }, computedUserset: { object: '', relation: 'repo_admin', }, }, }, ], }, }, owner: { this: {}, }, }, metadata: { relations: { owner: { directly_related_user_types: [{ type: 'org' }] }, admin: { directly_related_user_types: [{ type: 'user' }] }, }, }, }, { type: 'org', relations: { repo_admin: { this: {}, }, }, metadata: { relations: { repo_admin: { directly_related_user_types: [{ type: 'user' }] }, }, }, }, ], }} />
In this model, we have:
- added a new type
org
with one relationrepo_admin
. - added a new relation
owner
for typerepo
. - re-defined the relation
admin
forrepo
. A user can be defined as anadmin
directly, as we have seen above, or through therepo_admin from owner
clause. How this works, for example, is that ifuser
is related asrepo_admin
toorg:xyz
, andorg:xyz
is related asowner
torepo:1
, thenuser
is anadmin
ofrepo:1
.
With this model, we can add tuples representing that an org
is the owner
of a repo
. By adding following relationship tuples, we are indicating that the xyz organization is the owner of repositories with IDs 1
and 2
:
<WriteRequestViewer relationshipTuples={[ { user: 'org:xyz', relation: 'owner', object: 'repo:1', }, { user: 'org:xyz', relation: 'owner', object: 'repo:2', }, ]} />
Now, imagine we have a new user Becky. If we wanted to have Becky be the admin
of all repo
s without having to add one tuple per repo
, all we need to do is add one tuple that says that Becky is related as repo_admin
to org:xyz
.
<WriteRequestViewer relationshipTuples={[ { user: 'user:becky', relation: 'repo_admin', object: 'org:xyz', }, ]} />
We can now verify that Becky an admin
of all the repo
s owned by org:xyz
:
<CheckRequestViewer user={'user:becky'} relation={'admin'} object={'repo:1'} allowed={true} />
<CheckRequestViewer user={'user:becky'} relation={'admin'} object={'repo:2'} allowed={true} />
Suppose now that we want to prevent users from being an admin
of repo:1
via org:xyz
. We can delete one tuple:
<WriteRequestViewer deleteRelationshipTuples={[ { user: 'org:xyz', relation: 'owner', object: 'repo:1', }, ]} />
With this change, we may now verify that Becky is no longer an admin
of repo:1
.
<CheckRequestViewer user={'user:becky'} relation={'admin'} object={'repo:1'} allowed={false} />
<RelatedSection description="Check the following sections for more on how to model relationships between objects." relatedLinks={[ { title: 'Modeling Parent-Child Objects', description: 'Learn about how to cascade relationships from parent object to child object.', link: '../modeling/parent-child', id: '../modeling/parent-child.mdx', }, { title: 'Modeling Object to Object Relationships', description: 'Learn about modeling patterns on objects that are not specifically tied to a user.', link: '../modeling/building-blocks/object-to-object-relationships', id: '../modeling/building-blocks/object-to-object-relationships.mdx', }, { title: 'Modeling GitHub', description: 'An example of object to object relationships.', link: '../modeling/advanced/github', id: '../modeling/advanced/github.mdx', }, ]} />