How Record access policies control data access

Most interactions with Skedulo team data are done via GraphQL in the form of queries and mutations. Since GraphQL can traverse related objects and fields, record access policies need to handle how data queries and changes work.

Query objects with lookup relationships to other objects

This section discusses how data access is controlled when record access policies are acting on certain objects relative to objects it has lookup relationships with.

Optional lookups

Optional lookup fields are filtered out of results if they should not be visible. This is so that users can’t see restricted information by viewing a record that isn’t restricted, but that has a lookup join to one that is. Skedulo does this automatically to reduce the need for complex rules that require advanced knowledge of the data model.

Example:
Optional lookup example

Optional lookups are handled in this way through all levels of data queries, such that records that are restricted by a rule remain inaccessible even when they are optional lookups for records that are optional lookups on records that are accessible.

Example: Optional lookup example

Mandatory lookups

If a record is restricted by a rule and is a mandatory lookup for a record that is not explicitly restricted by a rule, then the record as a whole (including the parent record) will not be visible. This is so that users can’t see a record that has a mandatory relationship to a record that they can’t see.

Example: If you try and return a Jobs object that is not restricted, but there is a mandatory lookup to a Region object that is restricted, then the job is restricted as well.

Mandatory lookup example

Objects may have a mandatory “has-many” lookup relationship, for example, a job can have many job allocations or many job tags. In this case, only the records that the user is permitted to see will be returned.

Example: If you try and return a Jobs object that is not restricted, but there is a mandatory has-many lookup to a Job tag object that is restricted, then the job will be visible, but only permitted job tags will be visible.

Mandatory has-many lookup example

Mutations of records with record access policies

The following principles govern how data can be changed when there are record access policies enabled:

  1. You must be able to see the object you’re mutating (a query for the same object should be successful).

  2. You must still be able to see the object after you’ve mutated it.

  3. If you are performing a bulk mutation and any of the mutations fail points 1 or 2 above, then the whole bulk mutation will return an error.

Summary of the expected behavior for mutations with record access policies

For all items in the table that follow, being able to “see” an object implies that a query for the same record would be successful.

Mutation Behavior
Get You can only get objects that you can see.
Insert You can only insert objects that you will be able to see after it is created.
Update You can only perform updates on objects you can see and that you will still be able to see following the update.
Upsert You can only perform upserts on objects that you can see (if already existing) and that you are able to see after the upsert (new and existing objects).
Delete You can only delete objects you can see.

Note that if you insert or update the value of a lookup reference, you need to be able to see the referenced record. For example, if you are updating a Job to change the Account it is linked to, you need to be able to see the Account record. This behavior applies to Insert, Update, and Upsert.

Mutations of data with lookup relationships

  • Optional lookup relationships (for example, accountId on a job) behave slightly differently to how they work for queries as only the ID field is exposed on mutations. They are, however, still used to enforce that the object you are mutating (and any lookup field IDs you supply) are visible to you:
    • If you supply an ID that you cannot see (that is, a query for the lookup object with that ID would not be found), then the mutation will fail with an error.
    • If you request an ID that you cannot see, then it will be returned as null (as if it wasn’t set).
  • Mandatory lookup fields (for example, regionId on a job):
    • If you supply an ID that you cannot see, then the mutation will fail with an error.
    • If you request a record that you cannot see, then the mutation will fail with an error.
  • Mandatory has-many fields:
    • If you attempt a mutation on an object with a has-many relationship to an object you cannot see, for example, creating a JobAllocation for a Jobs object you cannot see, then the mutation will fail with an error.

GraphQL subscriptions

Record access policies that apply to a user will also apply to any data that is pushed to the user via a GraphQL subscription.

After a GraphQL subscription is established, it continues to use the same record access filters for the life of the subscription. This means that if the rules change, the subscription will not pick up those changes (unlike queries and mutations, which fetch the current rules for every request). Any changes in RAP rules will only be picked up when the subscription is re-established, for example, when the web app page is refreshed or its token expires.