Rôles et privilèges
Protéger les données tout en permettant un accès rapide et facile aux utilisateurs autorisés est un défi majeur pour les applications web. L'architecture de sécurité ORDA est implémentée au cœur de votre datastore et vous permet de définir des privilèges spécifiques à toutes les sessions utilisateur web ou REST pour les différentes ressources de votre projet (datastore, dataclasses, fonctions, etc.).
Vue d’ensemble
L'architecture de sécurité ORDA est basée sur les concepts de privilèges, d'actions de permission (lecture, création, etc.) et de ressources.
Lorsque les utilisateurs web ou REST sont enregistrés, leur session est automatiquement chargée avec les privilèges associés. Les privilèges sont assignés à la session en utilisant la fonction session.setPrivileges().
Chaque requête utilisateur envoyée dans la session est évaluée par rapport aux privilèges définis dans le fichier roles.json du projet.
Si un utilisateur tente d'exécuter une action et ne dispose pas des droits d'accès appropriés, une erreur de privilège est générée ou, en cas de permission de lecture manquante sur les attributs, ils ne sont pas envoyés.

Resources
Vous pouvez assigner des actions de permission spécifiques aux ressources suivantes dans votre projet :
- the datastore
- the dataclasses
- attributes (including computed and alias)
- functions of the data model classes
- singleton functions
Chaque fois qu'on accède à une ressource dans une session (quelle que soit la manière dont on y accède), 4D vérifie que la session dispose des autorisations appropriées et rejette l'accès s'il n'est pas autorisé.
Permissions
A permission is the ability to do an action on a resource. For example, execute the ds.myTable.myFunction() represents a permission. Permissions are defined for the project in the roles.json file. Each permission can be given to one or more privileges.
When no specific permission has been defined for a resource, access to the resource may be automatically unrestricted or restricted depending on the default mode defined for the project.
Actions de permission
Les actions disponibles sont liées à la ressource cible.
| Actions | datastore | dataclass | attribut | fonction du modèle de données ou fonction singleton |
|---|---|---|---|---|
| create | Créer une entité dans n'importe quelle dataclass | Créer une entité dans cette dataclass | Créer une entité avec une valeur différente de la valeur par défaut autorisée pour cet attribut (ignoré pour les attributs alias). | n/a |
| read | Lire les attributs de n'importe quelle dataclass | Lire les attributs de cette dataclass | Lire ce contenu d'attribut | n/a |
| update | Mettre à jour les attributs dans n'importe quelle dataclass. | Mettre à jour les attributs de cette dataclass. | Mettre à jour le contenu de cet attribut (ignoré pour les attributs alias). | n/a |
| drop | Supprimer des données dans n'importe quelle dataclass. | Supprimer des données dans cette dataclass. | Supprimer une valeur non nulle pour cet attribut (sauf pour les attributs alias et calculés). | n/a |
| execute | Exécuter n'importe quelle fonction du projet (datastore, dataclass, entity selection, entité, singleton) | Exécuter n'importe quelle fonction de dataclass. Les fonctions de dataclass, d'entité et d'entity selection sont considérées comme des fonctions de dataclass | n/a | Exécuter cette fonction |
| promote | n/a | n/a | n/a | Associe un privilège donné lors de l'exécution de la fonction. Le privilège est ajouté temporairement et supprimé à la fin de l'exécution de la fonction. Par mesure de sécurité, seul le process exécutant la fonction reçoit le privilège, et non toute la session. |
- Un alias peut être lu dès que les privilèges de la session autorisent l'accès à l'alias lui-même, même si les privilèges de la session n'autorisent pas l'accès aux attributs résolvant l'alias.
- Un attribut calculé est accessible même si les attributs sur lesquels il est construit ne font l'objet d'aucune autorisation.
- Vous pouvez assigner une action de permission à une classe singleton (type
singleton), auquel cas elle sera appliquée à toutes ses fonctions exposées, ou bien à une fonction de singleton (typesingletonMethod). - Vous pouvez définir/supprimer l'action promote dynamiquement pour un process web en utilisant les fonctions
promote()etdemote(). - Valeurs par défaut : dans l'implémentation actuelle, seul Null est disponible en tant que valeur par défaut.
- En mode REST force login, la fonction
authentify()est toujours exécutable par les utilisateurs guest, quelle que soit la configuration des permissions.
Le paramétrage des permissions nécessite d'être cohérent, en particulier les permissions update et drop ont également besoin d'une permission read (mais create n'en a pas besoin).
Inherited permissions
Une action de permission définie à un certain niveau est héritée par défaut aux niveaux inférieurs, mais plusieurs niveaux de permissions peuvent être définis :
- Une action de permission définie au niveau du datastore est automatiquement assignée à toutes les dataclass. L'action de permission execute définie au niveau du datastore s'applique à toutes les fonctions du projet, y compris aux fonctions singleton.
- Une action de permission définie au niveau dataclass remplace le paramétrage du datastore (le cas échéant). Par défaut, tous les attributs de la dataclass héritent des permissions de la dataclass.
- Contrairement aux permissions des dataclass, une action de permission définie au niveau de l'attribut ne remplace pas la permission de la dataclass parente, mais y est ajoutée. Par exemple, si vous avez attribué le privilège "général" à une dataclass et le privilège "détail" à un attribut de la dataclass, les deux privilèges, "général" et "détail", doivent être définis dans la session afin d'accéder à l'attribut.
Les permissions contrôlent l'accès aux objets ou fonctions du datastore. Si vous voulez filtrer les données de lecture selon certains critères, vous pouvez envisager d'utiliser les entity selections restreintes qui peuvent être plus appropriées dans ce cas.
Attribution de permissions aux fonctions de la classe ORDA
Lors de la configuration des permissions, les fonctions de classe ORDA sont déclarées dans l'élément applyTo en utilisant la syntaxe suivante :
<DataclassName>.<functionName>
Par exemple, si vous voulez appliquer une permission à la fonction suivante :
// cs.CityEntity class
Class extends Entity
Function getPopulation() : Integer
...
... vous devez écrire :
"applyTo":"City.getPopulation"
Cela signifie que vous ne pouvez pas utiliser les mêmes noms de fonctions dans les différentes classes ORDA (entité, entity selection, dataclass) si vous souhaitez que des privilèges leur soient attribués. Dans ce cas, vous devez utiliser des noms de fonction distincts. Par exemple, si vous avez créé une fonction "drop" dans les classes cs.CityEntity et cs.CitySelection, vous devez leur donner des noms différents tels que dropEntity() et dropSelection(). Vous pouvez ensuite écrire dans le fichier "roles.json" :
"permissions": {
"allowed": [
{
"applyTo": "City.dropEntity",
"type": "method",
"promote": [
"name"
]
},
{
"applyTo": "City.dropSelection",
"type": "method",
"promote": [
"name"
]
}
]
Privilèges et Rôles
Un privilège est la capacité technique d'exécuter des actions sur des ressources, tandis qu'un rôle est un privilège public destiné à être utilisé par un administrateur. Fondamentalement, un rôle rassemble plusieurs privilèges pour définir un profil utilisateur métier. Par exemple, "manageInvoices" pourrait être un privilège tandis que "secrétaire" pourrait être un rôle (qui inclut "manageInvoices" et d'autres privilèges).
Un privilège ou un rôle peut être associé à plusieurs combinaisons "action + ressource". Plusieurs privilèges peuvent être associés à une action. Un privilège peut inclure d'autres privilèges.
-
Vous créez des privilèges et/ou des rôles dans le fichier
roles.json(voir ci-dessous). Vous configurez leur portée en les assignant aux actions de permission appliquées aux ressources. -
Vous autorisez les privilèges et/ou les rôles pour chaque session utilisateur à l'aide de la fonction
.setPrivileges()de la classeSession.
Exemple
Pour permettre un rôle dans une session :
exposed Function authenticate($identifier : Text; $password : Text)->$result : Text
var $user : cs.UsersEntity
Session.clearPrivileges()
$result:="Your are authenticated as Guest"
$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))
$result:="Your are authenticated as "+$user.role
End if
End if
roles.json
The roles.json file describes the whole web security settings for the project. La syntaxe du fichier roles.json est la suivante:
| Nom de propriété | Type | Obligatoire | Description | ||
|---|---|---|---|---|---|
| privileges | Collection d'objets privilege | X | Liste de privilèges définis | ||
| [].privilege | Text | Nom de privilège | |||
| [].includes | Collection de chaînes | Liste de noms de privilèges inclus | |||
| roles | Collection d'objets role | Liste de rôles définis | |||
| [].role | Text | Nom de rôle | |||
| [].privileges | Collection de chaînes | Liste de noms de privilèges inclus | |||
| permissions | Object | X | Liste d'actions autorisées | ||
| allowed | Collection d'objets permission | Liste de permissions autorisées | |||
| [].applyTo | Text | X | Nom de ressource cible | ||
| [].type | Text | X | Type de ressource : "datastore", "dataclass", "attribute", "method", "singletonMethod", "singleton" | ||
| [].read | Collection de chaînes | Liste de privilèges | |||
| [].create | Collection de chaînes | Liste de privilèges | |||
| [].update | Collection de chaînes | Liste de privilèges | |||
| [].drop | Collection de chaînes | Liste de privilèges | |||
| [].execute | Collection de chaînes | Liste de privilèges | |||
| [].promote | Collection de chaînes | Liste de privilèges | |||
| restrictedByDefault | Boolean | If true, access to resources without explicit permissions is denied | |||
| forceLogin | Boolean | If true, enables "forceLogin" mode |
- Le nom de privilège "WebAdmin" est réservé à l'application. Il est déconseillé d'utiliser ce nom pour les privilèges personnalisés.
privilegesandrolesnames are case-insensitive.
Default File Location and Content
When a new project is created, a default roles.json file is generated at:
<project folder>/Project/Sources/
Voir la section Architecture .
Default content:
{
"privileges": [
],
"roles": [
],
"permissions": {
"allowed": [
{
"applyTo": "ds",
"type": "datastore",
"read": [],
"create": [],
"update": [],
"drop": [],
"execute": [],
"promote": []
}
]
},
"restrictedByDefault": false,
"forceLogin": false
}
Dans les versions précédentes, le fichier roles.json n'était pas créé par défaut. Depuis 4D 20 R6, lors de l'ouverture d'un projet existant qui ne contient pas de fichier roles.json ou les paramètres "forceLogin" : true, le bouton Activer l'authentification REST via la fonction ds.authentify() est disponible dans la page Fonctionnalités Web de la boîte de dialogue Paramètres. Ce bouton met automatiquement à jour vos paramètres de sécurité (vous devrez peut-être modifier votre code, voir cet article de blog).
In Qodly Studio for 4D, the login mode can be set using the Force login option in the Roles and Privileges panel.
Restriction Modes
The restrictedByDefault property configures how every resource are accessed when no specific permission is defined for it:
- Unrestricted mode (
restrictedByDefault: false): Resources without defined permissions are accessible to all requests. This mode is suitable for development environments where access can be gradually restricted. - Restricted mode (
restrictedByDefault: true): Resources without defined permissions are blocked by default. This mode is recommended for production environments where access must be explicitly granted.
- When creating a new project, the
restrictedByDefaultproperty is set to false in the roles.json file (see below). Keep in mind that this configuration is tailored for quick start and smooth development. In production environment, it is recommended to set therestrictedByDefaultandforceLoginproperties to true. - In projects converted from previous releases; when enabling access to Qodly Studio using the One-click configuration dialog, the
restrictedByDefaultproperty is added with value true in the roles.json file.
Recommended Configuration
Depending on your environment, the recommended settings are:
- Production: Set both
restrictedByDefaultandforceLoginto true. This ensures maximum security by requiring user authentication and explicitly defined permissions for resource access. - Development: Set both
restrictedByDefaultandforceLoginto false. This allows easier access during development and debugging, with the possibility to gradually apply restrictions.
Roles_Errors.json
Le fichier roles.json est analysé par 4D au démarrage. Vous devez redémarrer l'application pour que les modifications dans ce fichier soient prises en compte.
En cas d'erreur(s) lors de l'analyse du fichier roles.json, 4D charge le projet mais désactive la protection globale d'accès - cela permet au développeur d'accéder aux fichiers et de corriger l'erreur. Un fichier d'erreur nommé Roles_Errors.json est généré dans le dossier Logs du projet et décrit les lignes d'erreur. Ce fichier est automatiquement supprimé lorsque le fichier roles.json ne contient plus d'erreur.
Il est recommandé de vérifier au démarrage si un fichier Roles_Errors.json existe dans le dossier [Logs](.. Project/architecture.md#logs), ce qui signifie qu'il y a eu une erreur d'analyse et que les accès ne seront pas limités. Vous pouvez écrire par exemple :
If (Not(File("/LOGS/"+"Roles_Errors.json").exists))
…
Else // vous pouvez empêcher l'ouverture du projet
ALERT("The roles.json file is malformed or contains inconsistencies, the application will quit.")
QUIT 4D
End if
Exemple de configuration de privilèges
{
"forceLogin": true,
"restrictedByDefault": true,
"permissions": {
"allowed": [
{
"applyTo": "People",
"type": "dataclass",
"read": [
"viewPeople"
]
}
]
},
"privileges": [
{
"privilege": "viewPeople",
"includes": []
}
],
"roles": []
}