Part V: Access control¶
Now that we have the schema ready, lets go and try out the following sample query that fetches all articles along with the comments (from the comment table we created) using the comments relationship:
query fetch_article {
article {
title
comments {
comment
}
}
}
Go to the API-Explorer section in the API-Console and choose GraphQL
in the left panel:
Type the query and press the play button:
Once you’ve tried it, the query must have failed throwing an error like:
{
"errors": [
{
"path": "$[0].args.columns[1]",
"error": "role \"anonymous\" does not have permission to read relationship \"comments\"; no permission on table \"comment\"",
"code": "permission-denied"
}
]
}
This is because we have not set the permissions for the comments
table that we created.
By default, the data APIs can only be accessed by users with the admin
role. However,
our app users will not have the admin role assigned. So, we need to allow access to the data APIs for
roles other than admin
. This is handled by the permission layer of the data
microservice,
which lets you define row level and column level access control policies for all the roles and for the different query types.
In our app, what roles do we have other than admin
?
user
for logged in usersanonymous
for users who haven’t logged in.
We need to define permissions on all the tables that we have created so far for the user
and anonymous
roles.
Let’s look at the access conditions we should set up for our tables:
Role | Query type | Columns | Rows |
---|---|---|---|
anonymous | select | all columns | all rows |
anonymous | insert | – | not allowed |
anonymous | update | none | not allowed |
anonymous | delete | – | not allowed |
user | select | all columns | all rows |
user | insert | – | if user is the author |
user | update | title, content | if user is the author |
user | delete | – | if user is the author |
Role | Query type | Columns | Rows |
---|---|---|---|
anonymous | select | all columns | all rows |
anonymous | insert | – | not allowed |
anonymous | update | none | not allowed |
anonymous | delete | – | not allowed |
user | select | all columns | all rows |
user | insert | – | their own row |
user | update | name | their own row |
user | delete | – | their own row |
Role | Query type | Columns | Rows |
---|---|---|---|
anonymous | select | all columns | all rows |
anonymous | insert | – | not allowed |
anonymous | update | none | not allowed |
anonymous | delete | – | not allowed |
user | select | all columns | all rows |
user | insert | – | if user is giving the like |
user | update | none | not allowed |
user | delete | – | if user gave the like |
Role | Query type | Columns | Rows |
---|---|---|---|
anonymous | select | all columns | all rows |
anonymous | insert | – | not allowed |
anonymous | update | none | not allowed |
anonymous | delete | – | not allowed |
user | select | all columns | all rows |
user | insert | – | if user is writing the comment |
user | update | comment | if user wrote the comment |
user | delete | – | if user wrote the comment or is author of the article |
To summarize:
anonymous
role users can select (read) all the dataanonymous
role users cannot modify (insert/delete/update) any data.user
role users can select (read) all the data.user
role users can insert/delete “their own data” and update only certain fields once inserted.
To define “their own data”, we can describe a condition using the value of the X-Hasura-User-Id
header passed to
the data microservice by the API gateway.
The following are the conditions we will use while setting up the row level permissions described above:
Table | Definition | Condition | Representation |
---|---|---|---|
All tables | allow all rows | Without any checks | {}
|
article | user is author | user-id is equal to author_id |
{
"author_id": {
"$eq": "X-Hasura-User-Id"
}
}
|
author | user’s own row | user-id is equal to id |
{
"id": {
"$eq": "X-Hasura-User-Id"
}
}
|
like | user gave like | user-id is equal to user_id |
{
"user_id": {
"$eq": "X-Hasura-User-Id"
}
}
|
comment | user wrote comment | user-id is equal to user_id |
{
"user_id": {
"$eq": "X-Hasura-User-Id"
}
}
|
comment | user wrote comment or is author of article | user-id is equal to user_id or user-id is equal to article's author_id (this requires a relationship
called article to be defined first) |
{
"$or": [
{
"user_id": {
"$eq": "X-Hasura-User-Id"
}
},
{
"article": {
"author_id": {
"$eq": "X-Hasura-User-Id"
}
}
}
]
}
|
Defining permissions:¶
We can use the API console
UI to add permissions for our tables. Head to Data -> [table-name] -> Permissions to
see/modify the permissions on the table.
For example, let’s set the update
permissions for the user
role on the article
table:
The Permissions tab of the article
table should look like this:
Click on the Edit icon next to the user/update cell. It should open up an edit section like this:
Now, set the permissions as described above. It should eventually look like this:
Hit Save permissions to save our changes.
Similary, set permissions for all the cases we have described above.
Once you have set the permissions, you can try the anonymous query to fetch the articles along with comments and it will work because we have set anonymous read permissions on all the tables:
Next: Customise schema with Postgres views¶
Next, let’s head to Part VI: Customise schema with views.