Common access control patterns with Hasura GraphQL Engine
Introduction
Hasura GraphQL Engine
Hasura GraphQL Engine is a thin GraphQL server that sits on any Postgres database and allows you to CRUD the data with realtime GraphQL and access control.
Permissions
The roles in Hasura have nothing to do with the Postgres roles and users. These roles are implemented at the Hasura Layer.
-
Check constraint
: This is a boolean value built out of session variables, values of the fields of the row being inserted and all the logical operators. For example, to insert intousers
table, you want to set a condition like{ "id": { "_eq": "x-hasura-user-id" } }
. This condition enforces that whenever a row is being inserted to theusers
table, it can only be inserted if theid
of the row is equal to thex-hasura-user-id
value in the session variables. Columns
: You can restrict insertion to only particular columns (rest of them being null, default or being inferred fromcolumn presets
)Column Presets
: Column presets are values that you can set to be assigned to fields while they are being inserted. For example, when an entry is being inserted in ausers
table, we can take theid
fromx-hasura-user-id
session variable. This helps to avoid parsing the session information on the client.
Filter
: This is a boolean value built out of session variables and the values of the fields being selected. For example, in anarticles
table, you want all users to be allowed to query all published articles, but only their own unpublished articles. To implement that, you would add a filter like:
{
"or": [
{
"author_id": {
"_eq": "x-hasura-user-id"
}
},
{
"is_published": true
}
]
}
Columns
: Sometimes you want some roles to not have access to particular columns of the table. In such cases, you can explicitly choose which columns to allow and restrict.
Filter
: This is a boolean value built out of session variables and the values of the fields being updated. For example, in anarticles
table, you want users to modify only their own articles. To do that, your update filter would look like:
{
"author_id": {
"_eq": "x-hasura-user-id"
}
}
Columns
: You can restrict which columns can be updated. This comes handy in cases when you do not wantcreated_at
field to ever be updated, but you might want to update thetitle
field in thearticles
table.Column presets
: Like with insert, if you want to automatically update certain fields with certain values without it being explicitly mentioned in the request.
Filter
: Like withupdate
, the filter on delete permisson is a boolean condition which needs to be satisfied before the the row can be deleted.
Examples
query {
articles {
id
title
content
content_type
author {
id
name
}
comments {
body
id
author {
id
name
}
}
article_upvotes_aggregate {
aggregate {
count
}
}
article_downvotes_aggregate {
aggregate {
count
}
}
}
}
- Through check constraint: In the insert permission of the
articles
table, you can set a check constraint like{ "author_id": { "_eq": "x-hasura-user-id" } }
. This would ensure that the article would be inserted only if theauthor_id
in the insert payload matches thex-hasura-user-id
in the session variables. - Through column presets: In the insert permission of the
articles
table, you can disable inserting intoauthor_id
column and set a column preset such that theauthor_id
is automatically taken from thex-hasura-user-id
session variable. In this way, theauthor_id
would be inserted appropriately without the client needing to explicitly mention it.
moderator
: Should be allowed to updatetitle
andcontent
of without any filter.user
: Should be allowed to update thetitle
andcontent
with the filter{ "author_id": { "_eq": "x-hasura-user-id" } }
.
moderator
: Should be allowed to delete without checks.user
: Should be allowed to delete with the filter{ "author_id": { "_eq": "x-hasura-user-id" } }
CREATE VIEW article_downvote_count as
SELECT article_id,
count(*) AS downvotes
FROM article_downvotes
GROUP BY article_id;
{
"_and": [
{
"user" : {
"id": {
"_eq": "x-hasura-user-id"
}
}
},
{
"user": {
"karma": {
"_gte": 500
}
}
}
]
}
- The user is inserting an entry as themselves (author_id is equal to
x-hasura-user-id
) - The insert is successful only if the inserting user has karma greater than or eual to 500.
{
"_and": [
{
"user" : {
"id": {
"_eq": "x-hasura-user-id"
}
}
},
{
"content_type": {
"_in": ["text", "url"]
}
}
]
}
- The user is allowed to insert only as themselves. (author_id is equal to
x-hasura-user-id
) - In the article being inserted, the
content_type
field must be eithertext
orurl
.
Reference
Related reading