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 property cybernetics is defined in the concrete class for 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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
query allCharacters { character { name __typename ... on Human { cybernetics } } }
{ "errors": [ { "message": "WARN: Field 'cybernetics' from type 'Human' is removed as selection due to being queried without permissions, roles=[CharacterRead]", "locations": [ { "line": 6, "column": 15 } ] } ], "data": { "character": [ { "name": "Ratts Tyerell", "__typename": "Aleena" }, { "name": "Dexter Jettster", "__typename": "Besalisk" }, { "name": "Ki-Adi-Mundi", "__typename": "Cerean" }, { "name": "Mas Amedda", "__typename": "Chagrian" }, { "name": "Zam Wesell", "__typename": "Clawdite" }, { "name": "IG-88", "__typename": "Droid" }, { "name": "C-3PO", "__typename": "Droid" }, { "name": "R2-D2", "__typename": "Droid" }, { "name": "R4-P17", "__typename": "Droid" }, { "name": "BB8", "__typename": "Droid" }, { "name": "R5-D4", "__typename": "Droid" }, { "name": "Sebulba", "__typename": "Dug" }, { "name": "Wicket Systri Warrick", "__typename": "Ewok" }, { "name": "Poggle the Lesser", "__typename": "Geonosian" }, { "name": "Rugor Nass", "__typename": "Gungan" }, { "name": "Roos Tarpals", "__typename": "Gungan" }, { "name": "Jar Jar Binks", "__typename": "Gungan" }, { "name": "Obi-Wan Kenobi", "__typename": "Human", "cybernetics": [] }, { "name": "Leia Organa", "__typename": "Human", "cybernetics": [] }, { "name": "Darth Vader", "__typename": "Human", "cybernetics": [] }, { "name": "Wilhuff Tarkin", "__typename": "Human", "cybernetics": [] }, { "name": "Biggs Darklighter", "__typename": "Human", "cybernetics": [] }, { "name": "Beru Whitesun lars", "__typename": "Human", "cybernetics": [] }, { "name": "Luke Skywalker", "__typename": "Human", "cybernetics": [] }, { "name": "Owen Lars", "__typename": "Human", "cybernetics": [] }, { "name": "Han Solo", "__typename": "Human", "cybernetics": [] }, { "name": "Jek Tono Porkins", "__typename": "Human", "cybernetics": [] }, { "name": "Wedge Antilles", "__typename": "Human", "cybernetics": [] }, { "name": "Raymus Antilles", "__typename": "Human", "cybernetics": [] }, { "name": "Lando Calrissian", "__typename": "Human", "cybernetics": [] }, { "name": "Boba Fett", "__typename": "Human", "cybernetics": [] }, { "name": "Lobot", "__typename": "Human", "cybernetics": [] }, { "name": "Palpatine", "__typename": "Human", "cybernetics": [] }, { "name": "Mon Mothma", "__typename": "Human", "cybernetics": [] }, { "name": "Arvel Crynyd", "__typename": "Human", "cybernetics": [] }, { "name": "Shmi Skywalker", "__typename": "Human", "cybernetics": [] }, { "name": "Finis Valorum", "__typename": "Human", "cybernetics": [] }, { "name": "Ric Olié", "__typename": "Human", "cybernetics": [] }, { "name": "Padmé Amidala", "__typename": "Human", "cybernetics": [] }, { "name": "Anakin Skywalker", "__typename": "Human", "cybernetics": [] }, { "name": "Qui-Gon Jinn", "__typename": "Human", "cybernetics": [] }, { "name": "Mace Windu", "__typename": "Human", "cybernetics": [] }, { "name": "Quarsh Panaka", "__typename": "Human", "cybernetics": [] }, { "name": "Cliegg Lars", "__typename": "Human", "cybernetics": [] }, { "name": "Bail Prestor Organa", "__typename": "Human", "cybernetics": [] }, { "name": "Jango Fett", "__typename": "Human", "cybernetics": [] }, { "name": "Gregar Typho", "__typename": "Human", "cybernetics": [] }, { "name": "Dormé", "__typename": "Human", "cybernetics": [] }, { "name": "Cordé", "__typename": "Human", "cybernetics": [] }, { "name": "Dooku", "__typename": "Human", "cybernetics": [] }, { "name": "Jocasta Nu", "__typename": "Human", "cybernetics": [] }, { "name": "Rey", "__typename": "Human", "cybernetics": [] }, { "name": "Captain Phasma", "__typename": "Human", "cybernetics": [] }, { "name": "Poe Dameron", "__typename": "Human", "cybernetics": [] }, { "name": "Finn", "__typename": "Human", "cybernetics": [] }, { "name": "Jabba Desilijic Tiure", "__typename": "Hutt" }, { "name": "Saesee Tiin", "__typename": "Iktotchi" }, { "name": "Grievous", "__typename": "Kaleesh" }, { "name": "Taun We", "__typename": "Kaminoan" }, { "name": "Lama Su", "__typename": "Kaminoan" }, { "name": "Plo Koon", "__typename": "Keldor" }, { "name": "Luminara Unduli", "__typename": "Mirialan" }, { "name": "Barriss Offee", "__typename": "Mirialan" }, { "name": "Ackbar", "__typename": "Moncalamari" }, { "name": "San Hill", "__typename": "Muun" }, { "name": "Kit Fisto", "__typename": "Nautolan" }, { "name": "Nute Gunray", "__typename": "Neimodian" }, { "name": "Tion Medon", "__typename": "Pauan" }, { "name": "Yarael Poof", "__typename": "Quermian" }, { "name": "Greedo", "__typename": "Rodian" }, { "name": "Wat Tambor", "__typename": "Skakoan" }, { "name": "Nien Nunb", "__typename": "Sullustan" }, { "name": "Adi Gallia", "__typename": "Tholothian" }, { "name": "Shaak Ti", "__typename": "Togruta" }, { "name": "Ben Quadinaros", "__typename": "Toong" }, { "name": "Watto", "__typename": "Toydarian" }, { "name": "Bossk", "__typename": "Trandoshan" }, { "name": "Bib Fortuna", "__typename": "Twilek" }, { "name": "Ayla Secura", "__typename": "Twilek" }, { "name": "Sly Moore", "__typename": "Umbaran" }, { "name": "Dud Bolt", "__typename": "Vulptereen" }, { "name": "Chewbacca", "__typename": "Wookiee" }, { "name": "Tarfful", "__typename": "Wookiee" }, { "name": "Gasgano", "__typename": "Xexto" }, { "name": "Yoda", "__typename": "Yodasspecies" }, { "name": "Darth Maul", "__typename": "Zabrak" }, { "name": "Eeth Koth", "__typename": "Zabrak" } ] } }

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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
query allCharacters { character { name __typename ... on Human { cybernetics } } }
{ "errors": [ { "message": "WARN: Type 'Character' is constrained to [Human], roles=[HumanRead]", "locations": [ { "line": 2, "column": 11 } ] } ], "data": { "character": [ { "name": "Obi-Wan Kenobi", "__typename": "Human", "cybernetics": [] }, { "name": "Leia Organa", "__typename": "Human", "cybernetics": [] }, { "name": "Darth Vader", "__typename": "Human", "cybernetics": [ "Cybernetic right arm", "prosthetic arms and legs", "life-support system" ] }, { "name": "Wilhuff Tarkin", "__typename": "Human", "cybernetics": [] }, { "name": "Biggs Darklighter", "__typename": "Human", "cybernetics": [] }, { "name": "Beru Whitesun lars", "__typename": "Human", "cybernetics": [] }, { "name": "Luke Skywalker", "__typename": "Human", "cybernetics": [ "Prosthetic right hand" ] }, { "name": "Owen Lars", "__typename": "Human", "cybernetics": [] }, { "name": "Han Solo", "__typename": "Human", "cybernetics": [] }, { "name": "Jek Tono Porkins", "__typename": "Human", "cybernetics": [] }, { "name": "Wedge Antilles", "__typename": "Human", "cybernetics": [] }, { "name": "Raymus Antilles", "__typename": "Human", "cybernetics": [] }, { "name": "Lando Calrissian", "__typename": "Human", "cybernetics": [] }, { "name": "Boba Fett", "__typename": "Human", "cybernetics": [] }, { "name": "Lobot", "__typename": "Human", "cybernetics": [] }, { "name": "Palpatine", "__typename": "Human", "cybernetics": [] }, { "name": "Mon Mothma", "__typename": "Human", "cybernetics": [] }, { "name": "Arvel Crynyd", "__typename": "Human", "cybernetics": [] }, { "name": "Shmi Skywalker", "__typename": "Human", "cybernetics": [] }, { "name": "Finis Valorum", "__typename": "Human", "cybernetics": [] }, { "name": "Ric Olié", "__typename": "Human", "cybernetics": [] }, { "name": "Padmé Amidala", "__typename": "Human", "cybernetics": [] }, { "name": "Anakin Skywalker", "__typename": "Human", "cybernetics": [ "Cybernetic right arm" ] }, { "name": "Qui-Gon Jinn", "__typename": "Human", "cybernetics": [] }, { "name": "Mace Windu", "__typename": "Human", "cybernetics": [] }, { "name": "Quarsh Panaka", "__typename": "Human", "cybernetics": [] }, { "name": "Cliegg Lars", "__typename": "Human", "cybernetics": [] }, { "name": "Bail Prestor Organa", "__typename": "Human", "cybernetics": [] }, { "name": "Jango Fett", "__typename": "Human", "cybernetics": [] }, { "name": "Gregar Typho", "__typename": "Human", "cybernetics": [] }, { "name": "Dormé", "__typename": "Human", "cybernetics": [] }, { "name": "Cordé", "__typename": "Human", "cybernetics": [] }, { "name": "Dooku", "__typename": "Human", "cybernetics": [] }, { "name": "Jocasta Nu", "__typename": "Human", "cybernetics": [] }, { "name": "Rey", "__typename": "Human", "cybernetics": [] }, { "name": "Captain Phasma", "__typename": "Human", "cybernetics": [] }, { "name": "Poe Dameron", "__typename": "Human", "cybernetics": [] }, { "name": "Finn", "__typename": "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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
query getDroid { droid { id name } }
{ "errors": [ { "message": "ERROR: The Authorization JWT Token has a 'roles' claim '[HumanRead]' yet the GraphQL query is attempting to 'query' object 'Droid'", "locations": [ { "line": 2, "column": 11 } ] } ] }

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 Human-s and make sure the results are correct:

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
query allHuman { human { id name homeworld { id name } } }
{ "errors": [ { "message": "WARN: The Authorization JWT Token has roles '[HumanFromTatooineRead]' which have limited access to 'Human' - (where: [{homeworld: [{EXISTS: [{name: [{EQ: \"Tatooine\"}]}]}]}])", "locations": [ { "line": 2, "column": 8 } ] } ], "data": { "human": [ { "id": "https://swapi.co/resource/human/4", "name": "Darth Vader", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } }, { "id": "https://swapi.co/resource/human/9", "name": "Biggs Darklighter", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } }, { "id": "https://swapi.co/resource/human/7", "name": "Beru Whitesun lars", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } }, { "id": "https://swapi.co/resource/human/1", "name": "Luke Skywalker", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } }, { "id": "https://swapi.co/resource/human/6", "name": "Owen Lars", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } }, { "id": "https://swapi.co/resource/human/43", "name": "Shmi Skywalker", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } }, { "id": "https://swapi.co/resource/human/11", "name": "Anakin Skywalker", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } }, { "id": "https://swapi.co/resource/human/62", "name": "Cliegg Lars", "homeworld": { "id": "https://swapi.co/resource/planet/1", "name": "Tatooine" } } ] } }

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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
mutation createJemRayfield { create_Person(objects: [{rdfs_label: {value: "Jem Rayfield"}, birthDate: "2020-01-01"}]) { person { id name birthDate } affected_objects { kind ids } } }
{ "errors": [ { "message": "WARN: Field 'name' from type 'Person' is removed as selection due to being queried without permissions, roles=[PersonWriter]", "locations": [ { "line": 5, "column": 16 } ] }, { "message": "WARN: Field 'birthDate' from type 'Person' is removed as selection due to being queried without permissions, roles=[PersonWriter]", "locations": [ { "line": 6, "column": 16 } ] } ], "data": { "create_Person": { "person": [ { "id": "https://swapi.co/resource/b5fc3bb3-24ab-5e2e-b4c8-52bcbab63506", "name": null, "birthDate": null } ], "affected_objects": [ { "kind": "Person", "ids": [ "https://swapi.co/resource/b5fc3bb3-24ab-5e2e-b4c8-52bcbab63506" ] } ] } } }

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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
mutation { update_Person( objects: { birthDate: null }, where: {ID: "person_id"}) { person { id } affected_objects { kind ids } } }
{ "errors": [ { "message": "ERROR: Restricted an attempt at clearing field 'birthDate' value(s) in type 'Person', roles=[PersonWriter]", "locations": [ { "line": 4, "column": 25 } ] } ] }

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 } } }
{ "errors": [ { "message": "ERROR: The Authorization JWT Token has a 'roles' claim '[ReadOnly]' yet the GraphQL query is attempting to 'create' object 'Person'", "locations": [ { "line": 2, "column": 11 } ] } ] }

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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
mutation { delete_Person (where: {ID: "person_id"}) { person { id } } }
{ "errors": [ { "message": "ERROR: The Authorization JWT Token has a 'roles' claim '[PersonNoDelete]' yet the GraphQL query is attempting to 'delete' object 'Person'", "locations": [ { "line": 2, "column": 11 } ] } ] }

Or remove property values.

Loading...
https://swapi-platform.ontotext.com/graphql
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
mutation { update_Person( objects: { birthDate: null }, where: {ID: "person_id"}) { person { id } affected_objects { kind ids } } }
{ "errors": [ { "message": "ERROR: Restricted an attempt at clearing field 'birthDate' value(s) in type 'Person', roles=[PersonNoDelete]", "locations": [ { "line": 4, "column": 25 } ] } ] }

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

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

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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
mutation { delete_Person (where: {ID: "person_id"}) { person { id } affected_objects { kind ids } } }
{ "errors": [ { "message": "WARN: The Authorization JWT Token has roles '[PersonJemAdmin]' which have limited delete access to 'Person' - (where: [{EXISTS: [{name: [{EQ: \"Jem Rayfield\"}]}]}])", "locations": [ { "line": 2, "column": 11 } ] }, { "message": "WARN: The Authorization JWT Token has roles '[PersonJemAdmin]' which have limited access to 'Person.id' - (where: [{EXISTS: [{name: [{EQ: \"Jem Rayfield\"}]}]}])", "locations": [ { "line": 4, "column": 15 } ] } ], "data": { "delete_Person": { "person": [ { "id": "https://swapi.co/resource/b5fc3bb3-24ab-5e2e-b4c8-52bcbab63506" } ], "affected_objects": [ { "kind": "Person", "ids": [ "https://swapi.co/resource/b5fc3bb3-24ab-5e2e-b4c8-52bcbab63506" ] } ] } } }

The mutation will pass. You get some warnings due to the limited access of the role. 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
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImJNODctcWV3SldXTWRMRjIzNUtXUmluTlp4TDA0ZngzIn0.eyJhdWQiOiI2NzJjYTdiMy1jMzcyLTRkZjMtODJkOC05YTFhMGQ3ZDY4YzEiLCJleHAiOjE3MjkyNDk4MTksImlhdCI6MTY5ODE0NTgxOSwiaXNzIjoic3dhcGktcGxhdGZvcm0ub250b3RleHQuY29tIiwic3ViIjoiZTlmOWQzNGQtZThmMS00ODM1LTlkMzAtOWRjNmU5YjQ4ZmMzIiwianRpIjoiODZjNTIzYzEtYzQzNC00MWZiLWFkOTctYWU2MDY5MjgxYWM4IiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InJlYWRvbmx5dXNlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJSZWFkT25seVVzZXIiLCJhcHBsaWNhdGlvbklkIjoiNjcyY2E3YjMtYzM3Mi00ZGYzLTgyZDgtOWExYTBkN2Q2OGMxIiwicm9sZXMiOlsiUmVhZE9ubHkiXSwiZGF0YSI6e319.yVwuhsZQQgbk6ASaaA5iS2z_E7ThqPpMtYKfxjVcND8
true
mutation createPerson { create_Person(objects: [{rdfs_label: {value: "Random Person"}, birthDate: "2020-01-01"}]) { person { id } } }
{ "errors": [ { "message": "ERROR: The DELETE has been prevented as the selected objects of type 'Person' failed to match the constraints '(where: [{EXISTS: [{name: [{EQ: \"Jem Rayfield\"}]}]}])'", "locations": [ { "line": 2, "column": 11 } ] }, { "message": "WARN: The Authorization JWT Token has roles '[PersonJemAdmin]' which have limited delete access to 'Person' - (where: [{EXISTS: [{name: [{EQ: \"Jem Rayfield\"}]}]}])", "locations": [ { "line": 2, "column": 11 } ] }, { "message": "WARN: The Authorization JWT Token has roles '[PersonJemAdmin]' which have limited access to 'Person.id' - (where: [{EXISTS: [{name: [{EQ: \"Jem Rayfield\"}]}]}])", "locations": [ { "line": 4, "column": 15 } ] } ], "data": { "delete_Person": { "person": [], "affected_objects": [ { "kind": "Person", "ids": [] } ] } } }

You will see that the mutation gets blocked.