Skip to main content

User & access system

Exiry backend uses an opinionated access system. The system divides the Users into Roles, and every Role has many Accesses and every Access has its own Privileges.

In recap: a User hasOne Role hasMany [ Access has Privileges ]. Check out the GraphQL schema below for more details.

Example case: In this page, we'll be using a Sales management tool like a CRM for example, you can check a recap accesses and privileges table in the end.

Privileges: the 5 fundamentals

Every access in the system can have up to 5 fundamental privileges:

  • can view: Can view all the data in the store*
  • can view own: Can view only the data that he owns** in the store*
  • can create: can create new data in the store*
  • can edit: can update data in the store*
  • can delete: can delete data in the store*
Important

User can only Edit or Delete that he can view.
So if he has a canViewOwn, he will be - if granted - able to edit or delete his own data.

View and ViewOwn priority

canView has a priority on canViewOwn.
If a user has a canView privilege on an Access, canViewOwn is ignored.

Notes

* the store: can be any data storage like SQL databases.
** data owned: can be different depending on the data, for example: own Invoices in a CRM are Invoice edited by the User or Invoices of the customers that the sales-person (User) manages.

Accesses

An Access can be anything, depending on your design. But most of the time, an access represents a set of data, for example Invoices, Projects or Customers.

Not every Access needs to have the 5 fundamental privileges, for example, in the CRM app we can have an Access with 2 privileges (canView and canViewOwn) like Reports, dashboard, or bulk data exports.

Accesses have two String attributes name and slug. Both are in english and they do not change with user's UI language. The name is displayed in the Administration part of the UI and needs to be easily understandable, the slug is slugified automatically generated from the name. The developer can add an optional description field if the name brings confusion.

Example: Invoices

Let's say in continuity to our above context, we are building a Sales management tool like a CRM. We are going to have in database some data relative to invoices like:

  • tbl_invoices
  • tbl_invoices_items

And we'll be creating an Access named Invoices and every data delivered via our Query and Mutation relative to those two database tables will be protected by the Invoices access.

Roles

Roles are basically groups of users, most of the time, roles will be parallel to in-life roles, for example, if we are building a Sales management tool like a CRM, roles gone be:

  • General manager will have a 1 member for example
  • Sales manager will have a 1 member for example
  • Sales-person will have a 10 members for example
  • Accountant will have a 1 member for example

Recap example table

For Role General manager

AccessViewView OwnCreateEditDelete
Invoices--

For Role Sales manager

AccessViewView OwnCreateEditDelete
Invoices--

For Role Sales-person

AccessViewView OwnCreateEditDelete
Invoices

For Role Accountant

AccessViewView OwnCreateEditDelete
Invoices--
Role redundancy

The Role General manager and Sales manager have the same access and privileges in this example. In a real life situation, some roles can have similar accesses and privileges but generally not for there accesses.

GraphQL schema

type User {
...
role: Role
}
type Role {
id: ID!
name: String
accesses: [Access]
}
type Access {
id: ID!
name: String!
description: String
slug: String!
isPage: Boolean
privileges: Privileges
allowView: Boolean
allowViewOwn: Boolean
allowEdit: Boolean
allowCreate: Boolean
allowDelete: Boolean
}
type Privileges {
id: ID!
canView: Boolean
canViewOwn: Boolean
canEdit: Boolean
canCreate: Boolean
canDelete: Boolean
}