Complex Hasura Permissions Via Views #82
thecodedrift
announced in
Thunked
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
As we continue to explore the space of mentorship & career success at Aibex, one of our prototypes tackled the idea of focused group messaging; think Slack, but without the signing in to seventeen spaces just to get something done. To make a messaging app that focused on the needs of mentors and mentees required unique permissions. We wanted users to have access to any messages they were present for, even if they later ended up leaving the group. To use our Slack analogy, leaving a private channel shouldn’t cut you off from the information you were originally invited to see.
The prototype is currently running on Hasura, generating our GraphQL API from an existing Postgres database. We chose Hasura to both reduce the amount of API code we needed to write and to take advantage of the systems’ in-built webhook actions. When it came time to implementing our permissions via Hasura’s JSON language, we ran afoul of Hasura’s undocumented Rules for Rules (my term, not theirs).
X-Hasura-User-Id
)In a messaging app, any message could have one or more recipients. In the Aibex prototype specifically, we also wanted someone who joined a group chat to gain access to all prior messages in the chat. After a user left a chat, they could continue to see all the messages during their time in the group.
There is no amount of
or
,and
, nor_exists
that solves this by default. If the title didn't give it away, the solution lies in offloading all computational work to a postgres view, simplifying the Hasura permission JSON.The Computed Conditional Problem
We're going to begin with a set of tables, defining a relationship between users, groups, and the messages within:
A user has many groups and that group has many messages
The problem arises when we want to add our permissions to the
messages
table. Hasura's permission system allows us to access(messages).group.memberships
via its inbuilt relationship system.Setting up our permissions this way gets us halfway; a user who has a relationship to the message (via the groups table), would be allowed to see the message in question. However, if we want to use
joined_at
anddeparted_at
:Accessing the group through an Object Relationship will only let us test that the user was in the group at some point. By the time we've traversed the relationship to check the
departed_at
column, we've lost the ability to check the original row'screated_at
value. Variations that use_exists
suffer from the same problem; currently you cannot use a row's dynamic value in permissions.Enter the View
To make our permission problem with Hasura, we need some precomputed version of our messages table. One that doesn't need
(messages).group.memberships.departed_at / created_at
to work. Even better, if we have one row for every recipient of a message, we can then just ask that version of the data instead.One option would be to denormalize the data, creating copies of the message for every recipient, but doing so will create integrity problems down the line. Enter the view:
The end result will be a view with a row for every message recipient, containing a message id and a user id, filtered such that the member is either active in the group (
NULL
) or theirdeparted_at
happened after the message'screated_at
timestamp. Now our Hasura permissions will work as expected. When we tell Hasura there is a manual relationship betweenmessages
and our newmessage_recipients
view, it will link both objects together. We can then use the new relationship in our permission JSON. 🎉Where Views Go Wrong?
In practice, large datasets will make conditional permissions via views expensive. This is because a view is just a prior known subquery under the hood. There are optimizations you can do with heavy read data such as a materialized view, but those situations imply you’ve got both a substantial amount of traffic and a substantial amount of data. Before you throw all of this away though, take advantage of Hasura’s “Analyze” button. Missing an important index can have a huge performance cost and for most applications, views are perfectly ok.
And if you do outgrow the view, then you should consider denormalizing the data. Hasura Actions can take care of the denormalization for you. In our final example, there’s nothing that stops you from writing to a recipient table in response to new messages and users joining the group. For now though, you probably won’t need those optimizations.
Ultimately, views give Hasura’s rule engine superpowers. If you can write a query that selects for your permission, you can make a view. And if you can make a view, Hasura can understand it.
Additional Reading
Beta Was this translation helpful? Give feedback.
All reactions