Roles & Session variables
Rolesβ
Every table/view can have permission rules for users based on their role.
By default, there is an admin
role that can perform any operation on
any table.
You can define roles and then create permissions for each of these roles:
Examples:
user | A logged-in user | CRUD on data that belongs to them |
anonymous | A not logged-in user | Only read from some tables/views |
manager | A user that has access to other userβs data | CRUD on all users data |
See this page on how to configure permission rules.
Role-based schemas
For every role that you create, Hasura automatically publishes a different GraphQL schema that represents the right queries, fields, and mutations that are available to that role.
Dynamic session variablesβ
Permission rules can also refer to session variables. Session variables are key-value pairs returned from the authentication service for each request.
For example: If a user makes a request, the session token maps to a
user-id
. This user-id
can be used in a permission to show that
inserts into a table are only allowed if the user_id
column has a
value equal to that of user-id
, the session variable.
When you are constructing permission rules, however, there might be
several variables that represent the business logic of having access to
data. For example, if you have a SaaS application, you might restrict
access based on a client_id
variable. If you want to provide different
levels of access on different devices, you might restrict access based
on a device_type
variable.
Hasura allows you to create permission rules that can use any dynamic
variable that is a property of the request. All your dynamic variables
must follow the naming convention X-Hasura-*
.
Examples:
Example | Role | Condition | Permission expression |
---|---|---|---|
Allow access to user's own row |
|
|
|
Allow project admins access to anything that belongs to the project |
|
|
|
ABAC
Session variables are analogous to attributes in a typical attribute-based access control (ABAC) system.
Modelling Roles in Hasuraβ
General guidelines for modelling roles in Hasura.
Roles are typically modelled in two ways:
- Hierarchical roles: Access scopes are nested depending on available roles. Roles in GitHub for organisations is a great example of such modelling where access scopes are inherited by deeper roles:
- Flat roles: Non-hierarchical roles with each role requiring an independent access scope to be defined.
Roles in Hasura have to be defined in the latter way i.e. in a flat, non-hierarchical model.
To convert the above hierarchical roles model into the one expected by
Hasura, you will need to model roles as partially captured by the table
below (showing access permissions for the user
& org-member
roles, repositories
table and select
operation):
Role | Access Permissions | Example permission rule |
---|---|---|
user | Allow access to personally created repositories |
|
org-member | Allow access to personally created repositories and the organisation's repositories |
|
Making role-based user information availableβ
Effective permission rules require that information about which roles have access to which objects is available when processing the permission rule. Different users with the same role or the same user with different roles may have access to different sets of rows of the same table.
In some cases this is straightforward - for example, to restrict access
for authors to only their articles, a trivial row-level permission like
"creator_id": {"_eq": "X-Hasura-User-Id"}
will suffice. In others,
like our example in the previous section, this user information
(ownership or relationship) must be available for defining a
permission rule.
These non-trivial use cases are to be handled differently based on whether this information is available in the same database or not.
Relationship information is available in the same databaseβ
Let's take a closer look at the permission rule for the org-member
rule in the example from the previous section. The rule reads as "allow
access to this repository if it was created by this user or if this user
is a member of the organisation that this repository belongs to".
The crucial piece of user information that is presumed to be available in the same database and that makes this an effective rule, is the mapping of users (members) to organizations.
Since this information is available in the same database, it can be easily leveraged via Relationships in permissions (see this reference for another example of the same kind).
Relationship information is not available in the same databaseβ
When this user information is not available in the database that Hasura is configured to use, session variables are the only avenue to pass this information to a permission rule. In our example, the mapping of users (members) to organizations may not have been available in the same database.
To convey this information, a session variable, say
X-Hasura-Allowed-Organisations
can be used by the configured
authentication to relay this information. We can then check for the
following condition to emulate the same rule: is the organization that
this repository belongs to part of the list of the organizations the
user is a member of.
The permission for org-member
role changes to this:
{
"_or": [
{
"creator_id": {
"_eq": "X-Hasura-User-Id"
}
},
{
"organization_id": {
"_in": "X-Hasura-Allowed-Organisations"
}
}
]
}
Array session variables in permission rules
Support for using session variables for array operators like _in
,
_nin
, _has_any_keys
, _has_all_keys
is available in versions
v1.0.0-beta.3
and above.
When you use array operators such as _in
in the permissions builder in
the Hasura console, it will automatically open an array for your values.
If your session variable value is already an array, you can click the
[X-Hasura-Allowed-Ids]
suggestion to remove the brackets and set your
session variable in its place.
Format of session variablesβ
Session variables are currently expected to be Strings and should be encoded as Postgres's literals for the relevant type.
For example, in the above example, let's say creator_id
and
organisation_id
columns are of type integer
, then the values of
X-Hasura-User-Id
and X-Hasura-Allowed-Organisations
should be of
type integer
and integer[]
(an integer array) respectively. To pass
say a value 1
for X-Hasura-User-Id
, it'll be "1
" and if the
allowed organisations are 1
, 2
and 3
, then
X-Hasura-Allowed-Organisations
will be "{1,2,3}
". {}
is the syntax
for specifying arrays in Postgres.
The types and their formats are detailed
here. When in
doubt about the Postgres format for a type, you can always test it in
the SQL window. To check if s
is a valid literal for type t
then,
you can check it as follows:
select 's'::t;
If the above command returns data, then s
is a valid literal of type
t
. For example, to check if {hello,world}
is a valid format of type
text[]
, you can run:
select '{hello,world}'::text[];
JSON format
In future, we'll add support for passing session variables as JSON values where possible (i.e, auth webhook and JWT but not in headers).
Additional Resources
Enterprise Grade Authorization - Watch Webinar.