Inherited roles¶
Table of contents
Introduction¶
An inherited role is a way to create a new role which inherits permissions from two or more roles.
Once an inherited role is created, it can be treated as any other role i.e. can be given in X-Hasura-Role session variable.
Inherited roles are useful when you need to define multiple permission rules (may be overlapping) on schema objects and also for greater modularity in role management.
By default, inherited roles will try to inherit the permissions from its parent roles. If you’d rather like to have a different permission than the inherited one for a particular entity and role pair, then it can be done by creating a permission for the entity and role pair. After creating this permission, it will override the inherited permission, if any.
Note
Inherited roles cannot form cycles.
For example:
Suppose there are two inherited roles: inherited_role1, inherited_role2 and
two non-inherited roles: role1, role2 and:
- inherited_role1inherits from- role1and- inherited_role2
- inherited_role2inherits from- role2and- inherited_role1
The above setup won’t work because inherited_role1 and inherited_role2 form a cycle.
Supported from
Inherited roles will be supported for versions v2.0.0-alpha.4 and above. The inherited roles feature
is an experimental feature from the v2.0.0-alpha.4 till the v2.1.0-beta.1 version i.e it must be
explicitly toggled in order to be enabled. This can be done either by setting the env
var HASURA_GRAPHQL_EXPERIMENTAL_FEATURES or the server flag --experimental-features to inherited_roles.
After the v2.1.0-beta.1 version, inherited roles will be enabled by default in the graphql-engine.
Creating inherited roles¶
To add a new inherited role, edit the metadata/inherited_roles.yaml file adding the inherited role definition
like this:
- role_name: sample_inherited_role
  role_set:
    - user
    - editor
Apply the metadata by running:
hasura metadata apply
You can add a inherited role using the add_inherited_role metadata API:
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin
{
  "type": "add_inherited_role",
  "args": {
     "role_name":"sample_inherited_role",
     "role_set":[
        "user",
        "editor"
     ]
  }
}
How is the permission of the inherited role inherited?¶
1. Select Permissions¶
A select permission is comprised of the following things:
- Columns accessible to the role
- Row selection filter
- Limit
- Allow aggregation
- Scalar computed fields accessible to the role
Suppose there are two roles, role1 gives access to column C1 with row filter P1 and role2 gives access to columns C1 and C2 with row filter P2. Consider the following GraphQL query executed with an inherited role comprised of role1 and role2:
query {
  T {
    C1
    C2
  }
}
The above GraphQL query will be translated to the following SQL query.
select (case when (P1 or P2) then C1 else null end) as C1,
       (case when P2 then C2 else null end) as C2
from T
where (P1 or P2)
The other parameters of the select permission will be combined in the following manner:
- Limit - Maximum of the limits will be the limit of the inherited role
- Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
- Scalar computed fields - same as table column fields, as in the above example
Accessibility of a field for an inherited role¶
Accessibility of a field for an inherited role is defined as follows:
- When all the roles give access to a column C, thenCwill always be accessible.
- When not all, but some of the roles give access to the column Cthen the value of the columnCwill be outputed when the OR ofP1,P2....P(n)istrueand when it evaluates tofalse, the value of the columnCwill benull, wherePis the row filter of the select permissions in which columnCis given access to.
- When none of the roles give access to column C, it won’t be accessible to the inherited role.
Examples¶
Let’s take the example of an users table with the following columns:
- id- Int - Primary key
- name- Text
- email- Text
There are two roles defined namely employee and manager.
- User role - The user role will be able to access all columns of their row  when the session variable X-Hasura-User-Idis equal to theid.
- Anonymous role - The anonymous role will be able to access only the idandnamecolumns of all the users.
Let’s create a new inherited role called user_anonymous_inherited_role which inherits from the user and the anonymous roles.
- Executing the query as - userrole- POST /v1/graphql HTTP/1.1 Content-Type: application/json X-Hasura-Role: user X-Hasura-User-Id: 1 - query { users { id name email } } query { users { id name email } }{ "data": { "users": [ { "id": 1, "name": "alice", "email": "alice@xyz.com" } ] } }
- Executing the query as - anonymousrole- POST /v1/graphql HTTP/1.1 Content-Type: application/json X-Hasura-Role: anonymous - query { users { id name } } query { users { id name } }{ "data": { "users": [ { "id": 1, "name": "Alice" }, { "id": 2, "name": "Bob" }, { "id": 3, "name": "Sam" } ] } }
- Executing the query as - user_anonymous_inherited_rolerole- POST /v1/graphql HTTP/1.1 Content-Type: application/json X-Hasura-Role: user_anonymous_inherited_role X-Hasura-User-Id: 1 - query { users { id name email } } query { users { id name email } }{ "data": { "users": [ { "id": 1, "name": "Alice", "email": "alice@xyz.com" }, { "id": 2, "name": "Bob", "email": null }, { "id": 3, "name": "Sam", "email": null } ] } }- In the response of the query being executed with the - user_anonymous_inherited_rolerole, there are 3 rows returned and if we compare that to the queries executed as the- userand- anonymousroles, the results are unioned in the inherited role. But some of the fields have- nullvalues despite the value in the database not being- null. This can only happen with inherited roles when a column doesn’t have permission in the particular row. In the above example, we see that the- emailof “Bob” and “Sam” is- nullbut a non null value for “Alice”, this is because the “Alice” row is executed as the- userrole and the other rows are executed as the- anonymousrole which is why is why the value is- null.
- Suppose we have two tables - usersand- authorsand similarly two roles- userand- authorare defined. The- userrole doesn’t have permission to query the- authorstable and the- authorrole doesn’t have permission to query the- userstable. With only the- userand the- authorrole, we won’t be able to construct a query which fetches data from both the tables. This can be solved by creating an inherited role out of- userand- authorwhich can query both the tables in a single query.- POST /v1/graphql HTTP/1.1 Content-Type: application/json X-Hasura-Role: user_authors_inherited_role X-Hasura-User-Id: 1 - query { users { id name email } authors { id name followers } } query { users { id name email } authors { id name followers } }{ "data": { "users": [ { "id": 1, "name": "Alice", "email": "alice@xyz.com" } ], "authors": [ { "id": 1, "name": "Paulo Coelho", "followers": 10382193 } ] } }
2. Mutation and remote schema permissions¶
A mutation (insert, update and delete) or remote schema permission is inherited in the following manner:
Suppose there’s an inherited role (R) which inherits permissions from n parent roles namely
pr1, pr2, pr3 … prn. The permission for the role R on some entity can only be inherited when the
permission on the entity is the same for all its parent roles.
For example, if two insert permissions are configured in the following way:
- insert permission of role - pr1- { "type" : "pg_create_insert_permission", "args" : { "table" : "article", "source": "default", "role" : "pr1", "permission" : { "check" : { "author_id" : "X-HASURA-AUTHOR-ID" } } } } 
- insert permission of the role - pr2- { "type" : "pg_create_insert_permission", "args" : { "table" : "article", "source": "default", "role" : "pr2", "permission" : { "check" : { "author_id" : "X-HASURA-USER-ID" } } } } 
The check constraint is different in both the permissions and there’s no way to
resolve this conflict.
Whenever a conflict occurs while a role inherits from its parents,
then the metadata for that entity and role combination will be marked as inconsistent.
These can be seen by calling the get_inconsistent_metadata API.
Following the above example, the role R which is trying to inherit permissions from the
role pr1 and pr2 will be marked as inconsistent for the table permission of the table article.
This inconsistency is informational and can be ignored if the conflicting role entity pair is not going to be used. If this inconsistency needs to be resolved, then it can be done by adding a permission explicitly for the conflicting role entity pair.
3. Actions and Custom Function Permissions¶
Inheritance of permissions of actions and custom function work in the following manner:
If any of the parent roles have permission configured for a given action or custom function, then the inherited role will also be able to access the given action or remote schema.
 
                      