docs/docs/auth/authorization/roles-variables.mdx
import Thumbnail from '@site/src/components/Thumbnail'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
Every table or view can have permission rules defined for users based on user role. You define your own roles in the Hasura GraphQL Engine and then create permissions for each of them.
For example:
| Role | Description | Allowed Activity |
|---|---|---|
| anonymous | A user who is not logged-in | Only read from some restricted tables/views |
| user | A user who is logged in | CRUD on data that belongs to them |
| manager | A user that has access to other users' data | CRUD on all users' data |
See this section on how to configure permissions.
By default, there is an admin role that can perform any operation on any table. It can be used like any other
user role when making queries where you would like full unrestricted permissions. The alternative to this method is to
use the admin secret header.
For every role that you create, Hasura automatically publishes a different GraphQL schema that represents the right fields, queries and mutations that are available to that role so that users with that role will only see a schema which they are able to access.
New roles are created "on-the-fly" when a permission is configured for it.
<Tabs className="api-tabs"> <TabItem value="console" label="Console">In the Console, in Data -> [table] -> Permissions, enter a new role name and click on the <code>select</code> cell for the role in order to begin configuring permissions for that role. When the permissions are saved, the new role will be created.
<Thumbnail src="/img/auth/authorization_create-new-role_2-16-1.png" alt="Using boolean expressions to build rules" />
</TabItem> <TabItem value="cli" label="CLI">Using the CLI, you can create a new role by defining permissions for it in <b>metadata > databases > [database_name] -> tables -> [table.yaml]</b> file. Eg:
- table:
schema: public
name: products
select_permissions:
- role: user
permission:
columns: []
filter:
price:
_lt: 1000
Apply the metadata by running:
hasura metadata apply
By defining permissions for a role, the role is created.
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin
{
"type": "pg_create_select_permission",
"args": {
"source": "<db_name>",
"table": "products",
"role": "user",
"permission": {
"columns": "*",
"filter": {
"price": {
"_lt": 1000
}
}
}
}
}
Deleting a role can be done from the permissions summary section.
Copying a role and its permissions can be done from the permissions summary section.
Permissions usually incorporate session variables. Session variables are data returned from your authentication service for each request.
In JWT mode, session variables are encoded into the payload of the JWT token. In webhook mode, session variables are returned as a JSON object in the body of the response from the webhook.
:::info Session variable key format
Session variables are case-insensitive and Hasura Engine only has access to session variables beginning with
X-Hasura-.
:::
When you are constructing permission rules there might be several variables that represent the required 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. It is entirely up to you to decide what restrictions and permissions you
want to apply to your data.
Hasura allows you to create powerful permissions that can use any variable that is a property of the request.
Examples:
<table> <thead> <tr> <th width="25%">Example</th> <th width="15%">Role</th> <th width="30%">Condition</th> <th width="35%">Permission expression</th> </tr> </thead> <tbody> <tr> <td><p>Allow access to user's own row</p></td> <td><p><code>user</code></p></td> <td><p><code>user_id</code> column is equal to the user id session variable in a request</p></td> <td>{
"user_id": {
"_eq": "X-Hasura-User-Id"
}
}
{
"project_id": {
"_eq": "X-Hasura-Project-Id"
}
}
:::info Attribute-Based Access Control - ABAC
Session variables are analogous to attributes in a typical attribute-based access control (ABAC) system.
:::
Roles in Hasura are defined in a flat, non-hierarchical model.
Role systems can typically be modeled in two ways:
To convert the above GitHub hierarchical roles model into the one expected by Hasura, you will need to model roles
as partially captured by the table below which shows permissions for the user & org-member roles, repositories
table and select operation:
{
"creator_id": {
"_eq": "X-Hasura-User-Id"
}
}
{
"_or": [
{
"creator_id": {
"_eq": "X-Hasura-User-Id"
}
},
{
"organization": {
"members": {
"member_id": {
"_eq": "X-Hasura-User-Id"
}
}
}
}
]
}
Hasura Engine's 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 users to only their shopping carts, a
trivial row-level permission like "user_id": {"_eq": "X-Hasura-User-Id"} in the shopping carts table select
permission will suffice.
In others, like in the example below where we need to check whether the user is actually a member of the related organization. The user information (ownership or relationship) must be available to define a permission rule.
{
"_or": [
{
"creator_id": {
"_eq": "X-Hasura-User-Id"
}
},
{
"organization": {
"members": {
"member_id": {
"_eq": "X-Hasura-User-Id"
}
}
}
}
]
}
These non-trivial use cases are to be handled differently based on whether this information is available in the same database or not.
Let's take a closer look at the permission for the org-member rule in the example from the previous section. The
rule reads as "allow access to this data if it was created by this user or if this user is a member of the
organization that it 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).
When this user information is not available in the database that Hasura is configured to use, session variables on the request are the only way to pass this information to a permission rule. In our example, let's assume 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-Organizations can be passed by your
authentication service 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 within the list of the organizations the user is a
member of.
The permission for the org-member role changes to this:
{
"_or": [
{
"creator_id": {
"_eq": "X-Hasura-User-Id"
}
},
{
"organization_id": {
"_in": "X-Hasura-Allowed-Organizations"
}
}
]
}
:::info 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 show
an array builder UI for your values. If your session variable value is already provided array, you can click the [X-Hasura-Allowed-Ids] suggestion to remove the brackets and set your session variable in its place.
:::
Session variables are currently expected to only be strings and should be encoded based on Postgres's literals for the relevant type.
For example, in the above, let's say creator_id and organization_id columns are of type integer, then the
values of X-Hasura-User-Id and X-Hasura-Allowed-Organizations 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
organizations are 1, 2 and 3, then X-Hasura-Allowed-Organizations 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 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[];