RBAC Tutorial

What’s in this document?

This section of the documentation is an Role Based Access Control tutorial. To read and learn about the syntax and language for declaring RBAC within Semantic Object schemas, see the Role Based Access Control (RBAC) section within the Semantic Object Modeling language documentation.

Queries with RBAC

Let’s say we have defined the following role in the schema:

CharacterRead:
   description: "Role which can read all Character properties"
   actions: [
     "Character/*/read"
   ]

The user CharacterRead has permissions only over the abstract class Character. When querying, abstract classes permissions are resolved from the lowest concrete class up. The abstract class is constrained to query other classes that are inherited from the abstract class.

Since the properties degree and cybernetics are defined in the concrete classes for Droid and Human, the following request will pass but will contain errors for each field that cannot be read by CharacterRead role.

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAyMjk3MzMsImlhdCI6MTU5NzE1NzczMywiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiMGRkMGI5OGEtMTM5Yy00ZDE2LWFkZjAtYzY0ZDUxMDU5YzFjIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6ImNoYXJhY3RlcnJlYWR1c2VyQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6ImNoYXJhY3RlcnJlYWR1c2VyIiwiYXBwbGljYXRpb25JZCI6IjY3MmNhN2IzLWMzNzItNGRmMy04MmQ4LTlhMWEwZDdkNjhjMSIsInJvbGVzIjpbIkNoYXJhY3RlclJlYWQiXX0.0a8bJEKCcoigR2HnMEr9dltyGaa5hfbfyUufJQPXLb4
true
query allCharacters { character { name __typename ... on Droid { degree } ... on Human { cybernetics } } }

What if the role has permissions to view only Human class and we try to execute a query for Character class?

HumanRead:
   description: "Role which can read all Human properties"
   actions: [
     "Human/*/read"
   ]
Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAyMjk3NzksImlhdCI6MTU5NzE1Nzc3OSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiNGVjOGJjMTctNmM2ZS00ZTEyLWEzNTktOWU4YzUzNWE1M2FmIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6Imh1bWFucmVhZHVzZXJAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiaHVtYW5yZWFkIiwiYXBwbGljYXRpb25JZCI6IjY3MmNhN2IzLWMzNzItNGRmMy04MmQ4LTlhMWEwZDdkNjhjMSIsInJvbGVzIjpbIkh1bWFuUmVhZCJdfQ.vjnOA_5YgmZ93TDKBDmlAYcPzmeKgHMOgeHka4rSI18
true
query allCharacters { character { name __typename ... on Droid { degree } ... on Human { cybernetics } } }

You will notice that only humans are returned and all other characters have been omitted.

If we try to query Droid with the same role, then we will get an error message stating that we do not have access to this object:

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAyMjk3NzksImlhdCI6MTU5NzE1Nzc3OSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiNGVjOGJjMTctNmM2ZS00ZTEyLWEzNTktOWU4YzUzNWE1M2FmIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6Imh1bWFucmVhZHVzZXJAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiaHVtYW5yZWFkIiwiYXBwbGljYXRpb25JZCI6IjY3MmNhN2IzLWMzNzItNGRmMy04MmQ4LTlhMWEwZDdkNjhjMSIsInJvbGVzIjpbIkh1bWFuUmVhZCJdfQ.vjnOA_5YgmZ93TDKBDmlAYcPzmeKgHMOgeHka4rSI18
true
query getDroid { droid { id name degree } }

We can be even more specific with the permissions and use filters. Let’s take a look at this role:

HumanFromTatooineRead:
  description: "Role which can read properties of Human with homeworld Tatooine"
  actions: [
    'Human/*/read/(where: {homeworld: {name: {EQ: "Tatooine"}}})',
    'Planet/*/read'
  ]

It will give access only to the Human-s from Tatooine. The permission over Planet are needed in order to retrieve the planet of the Humans-s and make sure the results are correct:

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjA0NzcwNDgsImlhdCI6MTU5NzQwNTA0OCwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZGQ0ZjEwZjItZjJmNi00ODhiLWJjMGUtNTdkNjE4NTIwZGFmIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6Imh1bWFuZnJvbXRhdG9vaW5lcmVhZHVzZXJAZXhhbXBsZS54b20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiSHVtYW5Gcm9tVGF0b29pbmVSZWFkVXNlciIsImFwcGxpY2F0aW9uSWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJyb2xlcyI6WyJIdW1hbkZyb21UYXRvb2luZVJlYWQiXX0.SlV9CHH59KhAwF3_-DlDo6TSqQRPyd_3jiix4e4FBTU
true
query allHuman { human { id name homeworld { id name } } }

Mutations with RBAC

Create, update, and delete mutations allow you to perform selections as part of the response.

However, if the role has no read access, the response will ignore the selections.

For example if we have the following role:

PersonWriter:
  description: "Can write People and read their id."
  actions: [
    "Person/*/write",
    "Person/id/read"
  ]

This request will pass but will contain errors for each field that cannot be read by PersonWriter.

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAzMDg1ODUsImlhdCI6MTU5NzIzNjU4NSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZWZkYTAwNDYtN2ZlOC00ZDQ5LWI2MGQtMGZjM2U1NGQwYmY1IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InBlcnNvbndyaXRlcnVzZXJAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiUGVyc29uV3JpdGVyVXNlciIsImFwcGxpY2F0aW9uSWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJyb2xlcyI6WyJQZXJzb25Xcml0ZXIiXX0.VIa85WONlWS2fhfvmJl_GgIdq9jr2VMcwPE2J9oFRv0
true
mutation createJemRayfield { create_Person(objects: [{rdfs_label: {value: "Jem Rayfield"}, birthDate: "2020-01-01"}]) { person { id name birthDate } affected_objects { kind ids } } }

Note the id of the newly created Person. We will be using this id in the examples below.

It is important to note that a role with a property write action cannot delete or set a property to null | []. For example, run the following mutation replacing person_id with the ID of the person we just created in the previous example:

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAzMDg1ODUsImlhdCI6MTU5NzIzNjU4NSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZWZkYTAwNDYtN2ZlOC00ZDQ5LWI2MGQtMGZjM2U1NGQwYmY1IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InBlcnNvbndyaXRlcnVzZXJAZXhhbXBsZS5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiUGVyc29uV3JpdGVyVXNlciIsImFwcGxpY2F0aW9uSWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJyb2xlcyI6WyJQZXJzb25Xcml0ZXIiXX0.VIa85WONlWS2fhfvmJl_GgIdq9jr2VMcwPE2J9oFRv0
true
mutation { update_Person( objects: { birthDate: null }, where: {ID: "person_id"}) { person { id } affected_objects { kind ids } } }

The request will be rejected with Unauthorized errors, since setting birthDate to null is effectively deleting the birthDate property. The PersonWriter role actions explicitly states that this role can only write/update this property.

The GraphQL schema may set a property as optional, so you will need RBAC rules to manage controlled removal of such a property by roles with delete access.

If you do not have write permissions assigned and try to perform a mutation, then the mutation will fail and no data will be inserted, updated, or deleted.

ReadOnly:
   description: "Users which can read all Objects and properties"
   actions: [
    "*/*/read"
   ]
Loading...
https://swapi-platform.ontotext.com/graphql
true
mutation createJemRayfield { create_Person(objects: [{rdfs_label: {value: "Jem Rayfield"}, birthDate: "2020-01-01"}]) { person { id name birthDate } affected_objects { kind ids } } }

It is important to remember that notActions take precedence over actions. If we have a role such as:

PersonNoDelete:
  description: "Can read and update People."
  actions: [
    "Person/*/*"
  ]
  notActions: [
    "Person/*/delete"
  ]

Then you will not be able to delete objects.

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAzMDg2MjQsImlhdCI6MTU5NzIzNjYyNCwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiMzFiMWI0MGYtOWRkOC00YTdlLWFlYzgtMjQ1NTU2ZTY3Nzk1IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InBlcnNvbm5vZGVsZXRldXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJQZXJzb25Ob0RlbGV0ZVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUGVyc29uTm9EZWxldGUiXX0.KX4fBY676963eYkVVTGJaSncRPvAJYT_hVp0l00jAZw
true
mutation { delete_Person (where: {ID: "person_id"}) { person { id } } }

Or remove property values.

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAzMDg2MjQsImlhdCI6MTU5NzIzNjYyNCwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiMzFiMWI0MGYtOWRkOC00YTdlLWFlYzgtMjQ1NTU2ZTY3Nzk1IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InBlcnNvbm5vZGVsZXRldXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJQZXJzb25Ob0RlbGV0ZVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUGVyc29uTm9EZWxldGUiXX0.KX4fBY676963eYkVVTGJaSncRPvAJYT_hVp0l00jAZw
true
mutation { update_Person( objects: { birthDate: null }, where: {ID: "person_id"}) { person { id } affected_objects { kind ids } } }

Filters can be used for mutations as well. To illustrate this, let’s create a second Person:

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Imc5Vm1fbGlyaHptdlk1dklsS1dGMXZCV0pSSWtGZ3NtIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NTEzMTEyNDMsImlhdCI6MTU4ODIzOTI0MywiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiYTQwYjczMTAtMDQzYy00NzI0LWFmNWYtMTQ1YmU4MDFlMmM3IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InBlb3BsZWxvY2F0aW9uQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6InBlb3BsZWxvY2F0aW9uIiwiYXBwbGljYXRpb25JZCI6IjY3MmNhN2IzLWMzNzItNGRmMy04MmQ4LTlhMWEwZDdkNjhjMSIsInJvbGVzIjpbIlBlcnNvbkNvdW50cnlMb2NhdGlvbiJdfQ.1MUJDbnQeYdIkgrS2OYeQ34ZPnyKNBYOUNq84LpYVGk
true
mutation createGeorgeLucas { create_Person(objects: [{rdfs_label: {value: "George Lucas"}, birthDate: "1944-05-14"}]) { person { id name birthDate } affected_objects { kind ids } } }

Note the id of the second person as well.

Now try to delete first “Jem Rayfield” using the following role:

PersonJemAdmin:
  description: "Full permission over Jem Rayfield."
  actions: [
    'Person/*/*/(where: {name: {EQ: "Jem Rayfield"}})'
  ]

In order to do so, replace person_id with the id of Jem:

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAzMDg2NTIsImlhdCI6MTU5NzIzNjY1MiwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiNDdmOWQyOGItMzQzNS00MGNlLWFmOWYtZTQxNjk2OTViOTQyIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InBlcnNvbmplbWFkbWludXNlckBleGFtbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6IlBlcnNvbkplbUFkbWluVXNlciIsImFwcGxpY2F0aW9uSWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJyb2xlcyI6WyJQZXJzb25KZW1BZG1pbiJdfQ.nf0JzmVV4BU2ZqOD1PSI8Atc9bFEtEvm90EYGSfCGhM
true
mutation { delete_Person (where: {ID: "person_id"}) { person { id } affected_objects { kind ids } } }

The mutation will pass. Now run the same mutation but using the id of “George Lucas”. You will not be able to delete him, as the filter in the action will prevent it.

With the same role, you can try to create a Person with a name different from Jem Rayfield:

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IlVWWHMyZ2hidFhBSUtyc2V5QnduTVYzR0dBSEZaRzMwIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE2NjAzMDg2NTIsImlhdCI6MTU5NzIzNjY1MiwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiNDdmOWQyOGItMzQzNS00MGNlLWFmOWYtZTQxNjk2OTViOTQyIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InBlcnNvbmplbWFkbWludXNlckBleGFtbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6IlBlcnNvbkplbUFkbWluVXNlciIsImFwcGxpY2F0aW9uSWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJyb2xlcyI6WyJQZXJzb25KZW1BZG1pbiJdfQ.nf0JzmVV4BU2ZqOD1PSI8Atc9bFEtEvm90EYGSfCGhM
true
mutation createPerson { create_Person(objects: [{rdfs_label: {value: "Random Person"}, birthDate: "2020-01-01"}]) { person { id } } }

You will see that the mutation gets blocked.