Filtrez l’accès à vos données grâce à un système complet de permissions

Le filtrage de l’accès aux données est une fonctionnalité indispensable pour empêcher tout accès malveillant à votre application.

Jusqu’à présent, vous pouviez exposer ou non comme ressource REST une classe de données et certains de ses attributs. C’était déjà un moyen pratique de restreindre l’accès à vos données. Néanmoins, nous sommes ravis d’offrir dans la v19R8 un système puissant et entièrement personnalisable pour protéger vos données contre les utilisateurs non autorisés. Un système de protection de vos données dépend de la personne qui y accède et des données auxquelles elle accède.

Comme vous l’avez demandé lors des échanges avec les clients, vous pourrez désormais gérer de nombreux utilisateurs travaillant sur différentes activités et régler l’accès à vos données avec plusieurs niveaux de granularité, du plus général au plus précis.

Dans la continuité des sessions web évolutives, cette fonctionnalité vous permet de construire une application fiable et protégée.

HDI_Permissions

contexte général

Cette nouvelle fonctionnalité est basée sur les concepts ORDA et les sessions web (plus précisément sur les sessions web évolutives).

Elle couvre tous les processus web tels que les requêtes REST et les requêtes reçues sur un datastore distant mais aussi les processus web comme 4DACTION ou 4D tags.

principaux concepts

Dans votre application, vous pouvez désormais définir les permissions autorisées.

Une permission indique une ressource et des privilèges associés à des actions sur cette ressource.

Les ressources sont un attribut de classe de données, une fonction du modèle de données ORDA, une classe de données et le datastore entier.

Les actions sont : créer, lire, mettre à jour, supprimer, décrire, exécuter (une fonction), et promouvoir (une fonction).

une permission pour un attribut de classe de données

L’attribut personalNotes de la classe de données Records peut être lu par le privilège medicalAction.

Ressource Records.personalNotes Privilèges
Lire medicalAction

une permission pour une fonction de classe de données

La fonction deleteOldRecords() définie dans la classe de données Records peut être exécutée par le privilège administrer.

Ressource Records.deleteOldRecords() Privilèges
Exécuter administrer

une permission pour une classe de données

La classe de données Records peut être lue par les privilèges medicalAction et administrer, et les entités peuvent être créées par le privilège medicalAction.

Ressource Records Privilèges
Lecture medicalAction, administrer
Créer medicalAction

une permission pour l’ensemble du datastore

Le privilège administrer peut supprimer des entités dans n’importe quelle classe de données de la base de données.

Ressource ds Privilèges
Supprimer administrer

Pour mettre en place vos règles personnalisées en matière de permissions, il vous suffit de créer un fichier roles.json dans le dossier Project/Sources de votre application.

Certains rôles (c’est-à-dire un ensemble de privilèges) peuvent également être définis et associés par vous-même à chaque utilisateur de votre application.

N’oubliez pas que vous pouvez associer certains privilèges à une session Web évolutive. Ensuite, lorsque le système reçoit une demande, le contrôle est effectué en fonction des privilèges de l’utilisateur stockés dans la session Web et des actions autorisées pour ces privilèges dans le fichier roles.json.

Une erreur de permission est levée si l’action sur la ressource n’est pas autorisée.

le mécanisme en détail

Lorsqu’aucun privilège n’a été défini dans la session web, il s’agit d’une session invitée. Par défaut, toutes les données sont accessibles pour ce privilège invité. De même, si vous ne définissez aucune permission dans le fichier roles.json, toutes les données sont accessibles par n’importe qui.

Ensuite, les autorisations que vous avez configurées dans le fichier roles.json entrent en jeu et restreignent l’accès aux données.

Les permissions peuvent être configurées pour les ressources à plusieurs niveaux de granularité. Ces ressources sont listées ci-dessous par niveaux de granularité (du plus général au plus précis) :

– le datastore
– une classe de données
– un attribut de classe de données / une fonction de classe de données

Les privilèges mis en place à un niveau donné sont annulés ou complétés par des privilèges spécifiés à un niveau plus précis.

Le diagramme ci-dessous donne une représentation plus graphique des privilèges associés aux actions sur une ressource.

Le privilège invité peut exécuter des actions sur des ressources associées à aucun privilège. Vous limitez l’accès à vos données dès que vous associez un privilège à un couple ressource/action.

ASTUCE: si vous souhaitez que vos données soient inaccessibles par défaut, associez au datastore un privilège nobody pour toutes les actions et assurez-vous que ce privilège ne sera jamais mis en session.

entrer dans les détails avec des exemples

Travaillons avec ce modèle de données simple.

blank

 

Dans cette application, nous voulons mettre en œuvre ces règles :

  • seul le privilège administrer peut supprimer et créer des entités sur l’ensemble du datastore
  • la classe de données Patients ne peut être lue que par le privilège medicalAction
  • l’attribut Records.personalNotes ne peut être lu que par le privilège medicalAction (même si la classe de données est lisible par plusieurs autres privilèges)
  • la fonction deleteOldRecords() définie dans la classe de données Records n’est exécutable que par le privilège administrer
  • manipuler une fonction authenticate() exécutable par n’importe qui (privilège invité)

 

le privilège administrer peut supprimer et créer des entités sur l’ensemble du datastore.

Dans le fichier roles.json ci-dessous, nous avons défini un privilège administrer. Ce privilège est autorisé à :

  • supprimer
  • créer

des entités dans l’ensemble du datastore (toutes les classes de données).

Seul ce privilège administrer est autorisé à effectuer ces actions. Parce que par défaut, toutes les actions sont autorisées sur toutes les données, et que nous n’avons associé aucun privilège pour l’action lire pour le datastore, n’importe qui peut lire toutes les classes de données du datastore.

{"privileges" : [{"privilege" :"administrer"}],
 "roles" : [{}],
 "permissions" : {
  "allowed" : [{"applyTo" :"ds", "type" : "datastore", "drop" : ["administrer"], "create" : ["administrer"]}]
 }
}

la classe de données patients ne peut être lue que par le privilège medicalaction.

Dans le fichier roles.json ci-dessous, nous avons ajouté un privilège medicalAction.

Seul ce privilège medicalAction est autorisé à lire la classe de données Patients. Comme cette autorisation est mise en place au niveau de la classe de données, elle remplace les autorisations mises en place au niveau du datastore + les autorisations du privilège invité. Ainsi, seul le privilège medicalAction peut lire la classe de données Patients. Les autres classes de données sont lisibles par tout le monde.

{ "privileges" : [{"privilege" : "administrer"},{"privilege" :"medicalAction"}],
 "roles" : [ {} ],
 "permissions" : 
  {"allowed" : [
      {"applyTo" : "ds", "type" : "datastore", "drop" : ["administrer"], "create" : ["administrer"] },
      {"applyTo" :"Patients", "type" : "dataclass", "read" : ["medicalAction"] }
  ]
 }
}

L’attribut records.personalnotes n’est lisible que par le privilège medicalaction.

Dans cet exemple, nous avons ajouté un privilège readRecords. Comme le privilège readRecords est inclus dans le privilège medicalAction, ce dernier peut exécuter toutes les actions autorisées par le privilège readRecords.

Les privilèges readRecords et medicalAction peuvent lire la classe de données Records, mais l’attribut personalNotes ne peut être lu que par le privilège medicalAction.

{ "privileges" : [{"privilege" : "administrer"}, {"privilege" :"readRecords"}, {"privilege" :"medicalAction", "includes" : ["readRecords"] } } ],
 "roles" : [ {} ],
 "permissions" : {
  "allowed" : [
   {"applyTo" : "ds", "type" : "datastore", "drop" : ["administrer"], "create" : ["administrer"] },
   {"applyTo" : "Patients", "type" : "dataclass", "read" : ["medicalAction"] },
   {"applyTo" :"Records", "type" : "dataclass", "read" : ["readRecords"] },
   {"applyTo" :"Records.personalNotes", "type" : "attribute", "read" : ["medicalAction"] }
  ]
 }
}

La fonction deleteoldrecords de la classe de données records est exécutable uniquement par le privilège administrer.

Dans l’exemple ci-dessous, étant donné que seul l’administrateur du système doit être autorisé à supprimer les anciens enregistrements, nous avons ajouté pour le privilège administrer l’autorisation d’exécuter la fonction deleteOldRecords() définie sur la classe de données Records.

Seul ce privilège peut l’exécuter.

Nous avons également ajouté la permission pour le privilège administrer de lire la classe de données Records car la lecture des entités est nécessaire pour les supprimer.

{"privileges" : [ {"privilege" :"administrer"}, {"privilege" : "readRecords"}, {"privilege" : "medicalAction", "includes" : ["readRecords"] } } ],
 "roles" : [ {} ],
 "permissions" : {
  "allowed" : [
   {"applyTo" : "ds", "type" : "datastore", "drop" : [ "administrer"], "create" : [ "administrer"] },
   {"applyTo" : "Patients", "type" : "dataclass", "read" : ["medicalAction"] },
   {"applyTo" : "Records", "type" : "dataclass", "read" : ["readRecords","administrer"] },
   {"applyTo" : "Records.personalNotes", "type" : "attribute", "read" : ["medicalAction"] },
   {"applyTo" :"Records.deleteOldRecords", "type" : "method", "execute" : ["administrer" ] }
  ]
 }
}

Gérer une fonction d’authentification exécutable par un invité

Pour empêcher un utilisateur étranger d’exécuter une fonction dans notre application, nous avons restreint l’exécution des fonctions au niveau du datastore au privilège none (il n’est jamais mis dans la session web).

Dans notre système, nous avons une fonction authenticate() (définie dans la classe DataStore ) qui doit être exécutée par les utilisateurs pour entrer dans l’application. Nous avons ajouté la permission d’exécuter cette fonction authenticate().

Comme elle doit pouvoir être exécutée par n’importe qui, l’action d’exécution a été définie pour le privilège guest (invité). Nous avons également ajouté les privilèges hr pour empêcher la lecture de la classe de données Users par une personne étrangère à l’application.

La fonction authenticate() doit lire la classe de données Users pour vérifier l’existence et le mot de passe de l’utilisateur. Elle est donc promue avec le privilège hr. L’action de promotion ajoute un privilège dans la session web (uniquement pendant l’exécution de la fonction).

{"privileges" : [ {"privilege" : "administrer"}, {"privilege" : "readRecords"}, {"privilege" : "medicalAction", "includes" : ["readRecords"] }, {"privilege" :"hr"}, {"privilege" :"none"} ],
 "roles" : [ {} ],
 "permissions" : {
  "allowed" : [
   {"applyTo" : "ds", "type" : "datastore", "drop" : ["administrer"], "create" : ["administrer"], "execute" : ["none"] },
   {"applyTo" : "Patients", "type" : "dataclass", "read" : ["medicalAction"] },
   {"applyTo" : "Users", "type" : "dataclass", "read" : ["hr"] },
   {"applyTo" : "Records", "type" : "dataclass", "read" : ["readRecords", "administrer"] },
   {"applyTo" : "Records.personalNotes", "type" : "attribute", "read" : ["medicalAction"] },
   {"applyTo" : "Records.deleteOldRecords", "type" : "method", "execute" : ["administrer"] },
   {"applyTo" : "ds.authenticate", "type" : "method", "promote" : ["hr"], "execute" : ["guest"]
   }
  ]
 }
}

la phase d’autentication

Dans le fichier roles.json, vous pouvez définir des rôles : c’est-à-dire un ensemble de privilèges. Dans l’exemple ci-dessous, nous avons ajouté le rôle de La Secrétaire contenant les privilèges createPatient et readRecords.

Les utilisateurs associés à ce rôle peuvent exécuter toutes les actions autorisées pour les privilèges createPatient et readRecords (créer un nouveau patient et lire des dossiers).

{"privileges" : [ { "privilege" : "administrer" }, {"privilege" :"readRecords" }, {"privilege" : "medicalAction", "includes" : [ "readRecords"] }, {"privilege" : "hr"  }, {"privilege" : "none"},
  {"privilege" :"createPatient" } ],
 "roles" : [ {"role" :"La Secrétaire", "privileges" : ["createPatient","readRecords"] } } ],
 "permissions" : {
  "allowed" : [
   {"applyTo" : "ds", "type" : "datastore", "drop" : ["administrer"], "create" : ["administrer"], "execute" : ["none"] },
   {"applyTo" : "Patients", "type" : "dataclass", "read" : ["medicalAction"], "create" : ["createPatient"] },
   {"applyTo" : "Users", "type" : "dataclass", "read" : ["hr"] },
   {"applyTo" : "Records", "type" : "dataclass", "read" : ["readRecords", "administrer"] },
   {"applyTo" : "Records.personalNotes", "type" : "attribut", "read" : ["medicalAction"] },
   {"applyTo" : "Records.deleteOldRecords", "type" : "method", "execute" : ["administrer"] },
   {"applyTo" : "ds.authenticate", "type" : "method", "promote" : ["hr"], "execute" : ["guest"] }
  ]
 }
}

Dans votre application, vous devez associer chaque utilisateur à un rôle dans une classe de données (la classe de données Users dans cet exemple).

Voici la classe de données

blank

et les données

blank

 

Lors de la phase d’authentification, vous pouvez mettre les privilèges associés à ce rôle dans la session web.

Voici un exemple de fonction authenticate().

exposed Function authenticate($identifier : Text; $password : Text) : Text
	
var $user : cs.UsersEntity
	
Session.clearPrivileges()
	
$user:=ds.Users.query("identifier = :1"; $identifier).first()
	
If ($user#Null)
	If (Verify password hash($password; $user.password))
		Session.setPrivileges(New object("roles"; $user.role))
		return "Your are authenticated as "+$user.role
	Else 
		return "Your are authenticated as Guest"
	End if 
Else 
	return "Your are authenticated as Guest"
End if 

Lisez attentivement la documentation pour en savoir plus sur les permissions, et téléchargez l’IDH ci-dessus pour exécuter un exemple de démonstration.

N’hésitez pas à ouvrir des discussions sur le forum.

Avatar
- Product Owner - Marie-Sophie Landrieu-Yvert a rejoint l'équipe de 4D Product en tant que Product Owner en 2017. En tant que Product Owner, elle est en charge de rédiger les user stories puis de les traduire en spécifications fonctionnelles. Son rôle est également de s'assurer que l'implémentation de la fonctionnalité livrée répond au besoin du client.Marie-Sophie est diplômée de l'école d'ingénieur ESIGELEC et a commencé sa carrière en tant qu'ingénieur chez IBM en 1995. Elle a participé à divers projets (projets de maintenance ou de construction) et a travaillé en tant que développeur Cobol. Elle a ensuite travaillé en tant que concepteur UML et développeur Java. Dernièrement, ses principaux rôles étaient d'analyser et de rédiger des exigences fonctionnelles, de coordonner les équipes commerciales et de développement.