Privilèges
La protection des données tout en permettant un accès rapide et facile aux utilisateurs autorisés est un défi majeur pour les applications Web. The ORDA security architecture is implemented at the heart of your datastore and allows you to define specific privileges to all user sessions for the various resources in your project (datastore, dataclasses, functions, 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.
When users get logged, their session is automatically loaded with associated privilege(s). Les privilèges sont assignés à la session en utilisant la fonction session.setPrivileges()
.
Chaque requête utilisateur envoyée au sein de 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
You can assign specific permission actions to the following resources in your project:
- le datastore
- une dataclass
- un attribut (y compris calculé et alias)
- une fonction de classe du modèle de données
Each time a resource is accessed within a session (whatever the way it is accessed), 4D checks that the session has the appropriate permissions, and rejects the access if it is not authorized.
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.
- 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.
Actions de permission
Les actions disponibles sont liées à la ressource cible.
Actions | datastore | dataclass | attribut | fonction du modèle de données |
---|---|---|---|---|
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, entity) | 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 |
describe | Toutes les dataclass sont disponibles dans l'API /rest/$catalog | Cette dataclass est disponible dans l'API /rest/$catalog | Cet attribut est disponible dans l'API /rest/$catalog. | Cette fonction de dataclass est disponible dans l'API /rest/$catalog |
promote | n/a | n/a | n/a | Associe un privilège donné lors de l'exécution de la fonction. Le privilège est temporairement ajouté à la session 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. |
Notes :
- Un alias peut être lu dès que les privilèges de session permettent l'accès à l'alias lui-même, même si les privilèges de session ne permettent pas l'accès aux attributs résolvant l'alias.
- Il est possible d'accéder à un attribut calculé même s'il n'y a pas de permissions sur les attributs sur lesquels il est construit.
- Valeurs par défaut : dans l'implémentation actuelle, seul Null est disponible en tant que valeur par défaut.
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)
- La permission promote a également besoin de la permission describe.
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
Le fichier roles.json
décrit l'ensemble des paramètres de sécurité du projet.
Dans un contexte autre que Qodly (cloud), vous devez créer ce fichier à l'emplacement suivant : <project folder>/Project/Sources/
. Voir la section Architecture.
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 | String | 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 | String | 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 | String | X | Nom de ressource cible | ||
[].type | String | X | Type de [ressource](#resources) : "datastore", "dataclass", "attribute", "method" | ||
[].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 | |||
[].describe | 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 |
- 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.
- Les noms
privileges
etroles
sont insensibles à la casse.
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. An error file named Roles_Errors.json
is generated in the Logs
folder of the project and describes the error line(s). This file is automatically deleted when the roles.json
file no longer contains error(s).
It is recommended to check at startup if a Roles_Errors.json
file exists in the Logs folder, which means that there was a parsing error and that accesses will not limited. Vous pouvez écrire par exemple :
If (Not(File("/LOGS/"+"Roles_Errors.json").exists))
…
Else // you can prevent the project to open
ALERT("The roles.json file is malformed or contains inconsistencies, the application will quit.")
QUIT 4D
End if
QUIT 4D
End if
Initialisation des privilèges pour le déploiement
Par défaut, si aucun paramètre spécifique n'est défini dans le fichier roles.json
, les accès ne sont pas limités. Cette configuration vous permet de développer l'application sans avoir à vous soucier des accès.
Cependant, lorsque l'application est sur le point d'être déployée, une bonne pratique consiste à verrouiller tous les privilèges, puis à configurer le fichier pour n'ouvrir que les parties contrôlées aux sessions autorisées. Pour verrouiller tous les privilèges sur toutes les ressources, mettez le fichier roles.json
suivant dans votre dossier de projet (il inclut des exemples de méthodes) :
{
"privileges": [
{
"privilege": "none",
"includes": []
}
],
"roles": [],
"permissions": {
"allowed": [{
"applyTo": "ds",
"type": "datastore",
"read": [
"none"
],
"create": [
"none"
],
"update": [
"none"
],
"drop": [
"none"
],
"execute": [
"none"
],
"describe": [
"none"
],
"promote": [
"none"
]
},
{
"applyTo": "ds.loginAs",
"type": "method",
"execute": [
"guest"
]
},
{
"applyTo": "ds.hasPrivilege",
"type": "method",
"execute": [
"guest"
]
},
{
"applyTo": "ds.clearPrivileges",
"type": "method",
"execute": [
"guest"
]
},
{
"applyTo": "ds.isGuest",
"type": "method",
"execute": [
"guest"
]
},
{
"applyTo": "ds.getPrivileges",
"type": "method",
"execute": [
"guest"
]
},
{
"applyTo": "ds.setAllPrivileges",
"type": "method",
"execute": [
"guest"
]
}
]
}
}