Permission
With Permission, you can control the access to resources. By default, no one has access to the resources. We can grant read, write, or delete permissions to specific users, roles, or groups.
The Tailor DB supports both Type-level and Record-level permissions. Type-level permissions apply to all resources of a specified type, whereas Record-level permissions are specific to individual records of that type. Record-level permissions are also assigned by default when a record is created.
The Record-level permissions must be more restrictive than the permissions defined in the Type-level. If both levels of permissions are set, the record will be subject to both permission rules, which will be applied in an AND condition.
Here's a table describing resource access for each type of permission.
Permission Type | Resource Access | Description |
---|---|---|
Type-level | Create | Add new row to the table using |
Delete | Delete a row from the table using | |
Update | Update specific row in the table using | |
Read | Read data from a table by querying the | |
Admin | A user or role must have | |
Record-level | Update | Update a specific record in the table. |
Delete | Delete a specific record from the table. | |
Read | Read records of a specific type. | |
Admin | A user or role must have |
You must set Type-level
permissions to apply any Record-level
permissions to the type. Refer Apply record permission to learn more.
Type-level permission
With the Type-level permission, you can control access to a specific Type (corresponding to Table).
In the permission, you can set Create
, Delete
, Update
, Read
, and Admin
permissions.
Each permission field has an array of objects with Id
, Ids
and Permit
values.
Id
: Target audience to which the permission applies. This can be the ID of a user, role, or group, or one of the following special pre-defined variables.tailordb.#Everyone
: Applies to all anonymous userstailordb.#LoggedInUser
: Applies to the logged in user
Ids
: Specifies the target audiences to which the permission applies. You can add an array consisting of the ID of a user, role, group, or special pre-defined variables.Permit
:tailordb.#Permit.Allow
ortailordb.#Permit.Deny
. No need to settailordb.#Permit.Deny
for every out-of-scope audience, as the default permission rule restricts everyone.
Examples
- Allow only the user with ID
<user_id>
and role<role_id>
to update a specific row in the table.
<span><span style="color: var(--shiki-color-text)">TypePermission: {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">// ("<user_id>" AND "<role_id>")</span></span>
<span><span style="color: var(--shiki-color-text)"> Update: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Ids: [</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><user_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><role_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-color-text)">]</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
- Allow only the user with ID
<user_id>
and role<role_id>
or group of users with ID<group_id>
, to delete a specific row in the table.
<span><span style="color: var(--shiki-color-text)">TypePermission: {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">// ("<user_id>" AND "<role_id>") OR "<group_id>"</span></span>
<span><span style="color: var(--shiki-color-text)"> Delete: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Ids: [</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><user_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><role_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-color-text)">]</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><group_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ...</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
- If both
Id
andIds
are specified, theIds
setting takes precedence, andId
is ignored.
<span><span style="color: var(--shiki-color-text)">TypePermission: {</span></span>
<span><span style="color: var(--shiki-color-text)"> Update: [</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">// "<group_id>" is ignored.</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><group_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Ids: [</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><user_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)"><role_id></span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-color-text)">]</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ...</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Set up Type-level permission
You can set up Type-level permission in the CUE schema file using the following 2 steps.
-
Set permission in the schema file
-
Apply the changes using
tailorctl
Assume you have a Payroll
type resource that contains sensitive data such as pay amount.
Let's grant User A (id:11111111-1111-1111-1111-111111111111
) permissions for create, read, and admin operations on the Payroll type of resources.
1. Set permission in the schema file
You can set TypePermission
field in the type definition schema file.
<span><span style="color: var(--shiki-color-text)">Payroll: tailordb.#Type </span><span style="color: var(--shiki-token-keyword)">&</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> Description: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">Payroll information</span><span style="color: var(--shiki-color-text)">"</span></span>
<span><span style="color: var(--shiki-color-text)"> Fields: {</span></span>
<span><span style="color: var(--shiki-color-text)"> grossPay: {</span></span>
<span><span style="color: var(--shiki-color-text)"> Type: tailordb.#TypeInt</span></span>
<span><span style="color: var(--shiki-color-text)"> Description: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">Grosspay in payroll</span><span style="color: var(--shiki-color-text)">"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">// Define permission for create, delete and admin to user id: 11111111-1111-1111-1111-111111111111</span></span>
<span><span style="color: var(--shiki-color-text)"> TypePermission: tailordb.#TypePermissions </span><span style="color: var(--shiki-token-keyword)">&</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> Create: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">11111111-1111-1111-1111-111111111111</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> Delete: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">11111111-1111-1111-1111-111111111111</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> Admin: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">11111111-1111-1111-1111-111111111111</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
2. Apply the changes using tailorctl
<span><span style="color: var(--shiki-token-function)">tailorctl</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">workspace</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">apply</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-m</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">./workspace.cue</span></span>
<span></span>
We can now verify the Type-level permission behavior through the GraphQL queries.
To execute operations as the user, you need to create a user by mutation.
<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> createUser(</span></span>
<span><span style="color: var(--shiki-color-text)"> input: {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">name</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">"{USER_NAME}"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">email</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">"{USER_EMAIL}"</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">roles</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">"11111111-1111-1111-1111-111111111111"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> ){</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Make sure this user is created in the preferred identity provider.
Refer Auth service to setup the identity provider.
After creating the user, run the following command to login to the application:
<span><span style="color: var(--shiki-token-function)">tailorctl</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">workspace</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">app</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">login</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string)">-n</span><span style="color: var(--shiki-color-text)"> ${your-app-name} </span></span>
<span></span>
After successfully logging in, you will receive the authorization token.
You can now include this token in the HTTP Authorization
header to access protected resources.
To include the token in GraphQL Playground, navigate to the Headers
tab and add the token to the Authorization
header.
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"Authorization"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"Bearer <token>"</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
User A should be able to create Payroll resources with the following query
<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> createPayroll(input: { </span><span style="color: var(--shiki-token-string)">grossPay</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-constant)">100</span><span style="color: var(--shiki-color-text)"> }) {</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
The create
permission is granted to User A in the setting above, allowing the mutation query to be executed successfully.
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"data"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"createPayroll"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"id"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"<resource_uuid>"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
However, you cannot delete the Payroll resource as you haven't set up delete
permission in the setting. Notice that the permissions are correctly applied to the Payroll resource.
<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">deletePayroll</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> deletePayroll(id: </span><span style="color: var(--shiki-token-string-expression)">"<resource_uuid>"</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
It should return an error response like below:
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"errors"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"message"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"access denied"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"locations"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"line"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"column"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">2</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"path"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-string-expression)">"deletePayroll"</span><span style="color: var(--shiki-color-text)">]</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"data"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"deletePayroll"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">null</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Record level permission
While Type-level permission is applied against all resources of the type, Record-level permission works against the single resource.
There are 2 ways to set record level permissions:
- Setting the
RecordPermission
property when defining a model. - Sending a
change<type_name>
GraphQL query to update permissions on a live application.
Apply RecordPermission
property
We can set RecordPermission
field in the type definition schema file.
In the permission, you can set Create
, Delete
, Update
, Read
, and Admin
permissions.
Each permission field has an array of objects with Id
, Ids
and Permit
values like the Type-level
permission.
Permissions are applied when the defined events occur.
You cannot define RecordPermission
on its own. It must always be defined with the Type-level
permission and works within the scope allowed by the Type-level
permission.
<span><span style="color: var(--shiki-color-text)">Payroll: tailordb.#Type </span><span style="color: var(--shiki-token-keyword)">&</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> Description: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">Payroll information</span><span style="color: var(--shiki-color-text)">"</span></span>
<span><span style="color: var(--shiki-color-text)"> Fields: {</span></span>
<span><span style="color: var(--shiki-color-text)"> grossPay: {</span></span>
<span><span style="color: var(--shiki-color-text)"> Type: tailordb.#TypeInt</span></span>
<span><span style="color: var(--shiki-color-text)"> Description: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">Grosspay in payroll</span><span style="color: var(--shiki-color-text)">"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">// Define permission for create, delete and admin to `Role` id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</span></span>
<span><span style="color: var(--shiki-color-text)"> TypePermission: tailordb.#TypePermissions </span><span style="color: var(--shiki-token-keyword)">&</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> Create: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> Delete: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> Admin: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-comment)">// Define permission for update and read to `User` id: 11111111-1111-1111-1111-111111111111</span></span>
<span><span style="color: var(--shiki-color-text)"> RecordPermission: {</span></span>
<span><span style="color: var(--shiki-color-text)"> Update: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">11111111-1111-1111-1111-111111111111</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> Read: [</span></span>
<span><span style="color: var(--shiki-color-text)"> {Id: </span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-string-expression)">11111111-1111-1111-1111-111111111111</span><span style="color: var(--shiki-color-text)">"</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> Permit: tailordb.#Permit.Allow}</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
If there are two users, here is the behavior for each:
UserA
(id: 11111111-1111-1111-1111-111111111111)
UserB
(id: 22222222-2222-2222-2222-222222222222)
Both users have the same role (id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee).
In the case of the above example, here is the behavior of each:
Read
:Only
UserAcan read
payroll records but UserB cannot read.Update
:Both
userscannot update
payroll records because the role id: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee is not allowed to update on theType-level
permission.Delete
:Both
userscan delete
.
Apply record level permission through GraphQL query
In the Payroll example, changePayroll
can change permissions for the single resource of Payroll by specifying the resource ID and permissions.
To use any change<type_name>
mutation, the user need to have an admin
permission at the type level.
We can verify the Record-level permission behavior through the GraphQL queries in the following steps:
- Create a new Payroll resource
- Read the resource
- Change the
read
permission at the Record level. - Retry to read the resource
1. Create a new Payroll resource
First, we'll need to create a new Payroll resource by executing the following query.
<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">createPayroll</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> createPayroll(input: { </span><span style="color: var(--shiki-token-string)">grossPay</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-constant)">100</span><span style="color: var(--shiki-color-text)"> }) {</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Use the created resource uuid in the next step.
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"data"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"createPayroll"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"id"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"<resource_uuid>"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
2. Read the resource
Using the resource UUID created in the previous step, User A can read the resource of Payroll as the Type-level read
permission is set for the Payroll Type. See Type-level permission setting.
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> payroll(id: </span><span style="color: var(--shiki-token-string-expression)">"<resource_uuid>"</span><span style="color: var(--shiki-color-text)">) {</span></span>
<span><span style="color: var(--shiki-color-text)"> grossPay</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
It should return a successful response like below:
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"data"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"payroll"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"grossPay"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">100</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
3. Change permission for read
Let's change the Record-level permission by changePayroll
mutation. The following query set permission to deny read
access to the resource created in the 1st step. See Examples to learn about different ways to set permissions.
<span><span style="color: var(--shiki-token-keyword)">mutation</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">changePermission</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> changePayroll(</span></span>
<span><span style="color: var(--shiki-color-text)"> id: </span><span style="color: var(--shiki-token-string-expression)">"<resource_uuid>"</span></span>
<span><span style="color: var(--shiki-color-text)"> read: [{ </span><span style="color: var(--shiki-token-string)">id</span><span style="color: var(--shiki-color-text)">: </span><span style="color: var(--shiki-token-string-expression)">"<user_uuid>"</span><span style="color: var(--shiki-color-text)">, </span><span style="color: var(--shiki-token-string)">permit</span><span style="color: var(--shiki-color-text)">: deny }]</span></span>
<span><span style="color: var(--shiki-color-text)"> )</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
4. Retry to read the resource
Let's verify the permission behavior by running the following query:
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> payroll(id: </span><span style="color: var(--shiki-token-string-expression)">"<resource_uuid>"</span><span style="color: var(--shiki-color-text)">) {</span></span>
<span><span style="color: var(--shiki-color-text)"> grossPay</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
As we set the read
permission to deny
for the specified user, the user can no longer read the resource.
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"errors"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"message"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"failed to access to record with id = {<your_domain_name> Payroll} <resource_uuid>"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"locations"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"line"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">1</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"column"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">16</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"path"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span><span style="color: var(--shiki-token-string-expression)">"payroll"</span><span style="color: var(--shiki-color-text)">]</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"data"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"payroll"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">null</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Getting permission
<type_name>Permission
is an auto-generated API, which returns the permissions set in the resource based on the provided resource ID.
If the RecordPermission
property is set for the model, the GraphQL query below will return the permissions for the provided resource ID; otherwise, the query will return null.
Here is a sample query and response:
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> taskPermission(id: </span><span style="color: var(--shiki-token-string-expression)">"c3e9313b-ef80-45da-a5b2-f6d77810dd92"</span><span style="color: var(--shiki-color-text)">) {</span></span>
<span><span style="color: var(--shiki-color-text)"> read {</span></span>
<span><span style="color: var(--shiki-color-text)"> permit</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> update {</span></span>
<span><span style="color: var(--shiki-color-text)"> permit</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> delete {</span></span>
<span><span style="color: var(--shiki-color-text)"> permit</span></span>
<span><span style="color: var(--shiki-color-text)"> id</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"data"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"taskPermission"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"read"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> [</span></span>
<span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"permit"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"deny"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"id"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"11111111-1111-1111-1111-111111111111"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> ]</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>