Coupon - partie 3¶
Cette leçon aborde la question des droits d'accès et des permissions qui s'y rapportent. Nous continuons à travailler sur l'application "coupon" mise en place durant les leçons "03 - coupon1" et "04 - coupon2".
Introduction¶
L'application "Coupon" mise en place lors des leçons précédente ne gère aucun droit d'accès : tous les utilisateurs peuvent accéder à tous les coupons et tous les magasins, en créer, les modifier, les supprimer.
D'autre part, les dossiers sont toujours modifiables, quel que soit le statut. Nous allons commencer par mettre en place une gestion des permissions en fonction du statut du coupon.
Nomenclature
Les policies d'Ewt font intervenir 4 notions qu'il peut être nécessaire de clarifier:
1) Permissions : Les permissions indiquent si une donnée est
disponible en lecture et/ou en écriture: les types de permissions
possibles sont donc read
ou write
2) Actions : Les actions font référence aux commandes envoyées par
un client (un navigateur) pour agir sur un dossier ou sur le moteur
lui-même. Le moteur supporte différentes actions et la liste est
susceptible de s'accroître au fil des versions de Ewt. On peut par
exemple citer les actions suivantes: addTuple
, admin
, arrange
,
close
, create
, clone
, delete
, delTuple
, dummy
, open
,
reset
, save
, script
, setState
, setLocale
, setStyle,
setView`.
3) Transitions : Les transitions sont des changements d'état d'un dossier
4) Operations : Les opérations sont des traitements propres à l'application, mais non interprétés par Ewt. Il s'agit d'une possibilité offerte à l'application pour apposer des policies à des processus qui lui sont spécifiques. On peut par exemple imaginer une opération "reindexCatalog" qui est un processus métier. On peut alors associer des policies à cette opération, bien qu'il ne s'agisse pas d'une opération du moteur.
Activation des policies¶
En premier lieu, nous devons activer le système de policies afin d'avoir une gestion de droits.
Qu'est-ce qu'une policy ?
Les policies sont largement décrites dans la documentation de référence. En
bref, il s'agit de règles permettant de gérer les droits d'accès. Les policies
peuvent être définies soit pour des groupes d'individus (on parle de policy
d'identité) soit pour des modèles, des groupes ou des champs (on parle alors
de policy de ressource).
Comment active-t-on le système de gestion des policies ?
Le système de gestion de policies s'active dès lors que des policies sont
définies dans l'application et/ou que la configuration de l'application
référence une policy. Nous allons effectuer ces deux opérations.
Mise en place d'une policy de base¶
Nous allons mettre en place une policy de base.
👉 Créez un répertoire policies
dans le répertoire principal de
l'application coupon
et ajoutez-y un fichier main.xml
ayant le
contenu suivant:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Vous pouvez également nommer le fichier main.policy
si vous souhaitez lui
donner une extension plus parlante.
Que fait cette policy ?
Cette policy accorde une permission de lecture seule aux utilisateurs
possédant le rôle EWT
. Elle autorise également l'usage des actions dummy
et save
.
Pour rappel, dans la leçon "01 - intro", nous avons configuré le serveur
tomcat de sorte que le login user
reçoive le rôle EWT
(nous avons
fait cela au niveau du fichier tomcat-users.xml
dans la config de Tomcat).
Il s'agit d'un rôle technique nécessaire pour accéder à l'application. Ce
rôle est référencé au niveau du fichier web.xml
d'Ewt en tant que
contrainte d'authentification (élément <auth-constraint>
), ce qui fait
qu'il est attendu par le serveur d'application: si un utilisateur
s'authentifie sur le serveur d'application mais ne possède pas ce rôle, il
recevra une erreur 403.
L'action dummy
est une action fictive qui n'effectue aucun traitement.
Elle n'en est pas pour autant inutile. En effet, lorsque l'on effectue une
requête GET sur le moteur, c'est-à-dire lorsque l'on accède à une
application au moyen d'une URL, le moteur vérifie que le client est autorisé
à effectuer au moins une action, quelle qu'elle soit. En accordant un droit
sur dummy
, on remplit cette condition sans que cela n'ait d'impact métier
sur une application, du fait que dummy
ne fait rien.
L'action save
est une action qui traite un formulaire html et close
est
une action qui ferme un dossier ouvert. On accorde le droit sur ces actions
car elles sont nécessaires à la navigation dans une application, même
lorsque celle-ci est en lecture seule.
👉 Effectuez un reset
de l'application.
Si vous avez lancé la commande reset
depuis la page d'accueil de
l'application, vous ne devriez pas remarquer grand chose. Par contre si vous
avez lancé la commande depuis un dossier, vous remarquerez que tous les
champs du dossier sont passés en lecture seule. Seule l'action "Fermer"
reste disponible (étant donné qu'elle est autorisée par la policy main
).
L'action "Enregistrer" a disparu malgré le fait que l'action save
est
autorisée. Cela vient du fait que la policy n'accorde pas la permission
write
.
👉 Depuis la page d'accueil, lancez une recherche en cliquant sur le bouton "Rechercher". Un message d'erreur signalant que l'opération n'est pas permise devrait apparaître.
Élément <appliesTo>
Au début de ce chapitre, nous avons évoqué les policies d'identité et les
policies de ressource. La policy main
mise en place ici est une policy
d'identité. Les policies d'identité contiennent une balise <appliesTo>
dans laquelle on énumère les références de sujets auxquels la policy doit
s'appliquer. Nous avons référencé le rôle EWT
. Les policies d'identité
sont automatiquement évaluées du moment que le sujet connecté correspond
à l'une des conditions <appliesTo>
de la policy.
Dans le cas des policies de ressources il en va différemment. Une policy
de ressource doit être référencée par une ressource. Prenons le cas de
la policy main
définie en tant que policy de ressource:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
La policy de ressource n'intègre pas d'élément <appliesTo>
. Par
conséquent, comment faire si nous souhaitons appliquer cette policy à
l'application ? Il faut pour cela ajouter une référence au niveau du
fichier de configuration. Cela se fait en ajoutant la section suivante
dans le fichier config.xml
:
1 2 3 |
|
Mise en place d'une policy d'identité¶
Nous avons mis en place une policy de base qui a eu pour effet de fortement restreindre les droits de l'utilisateur "user" sur l'application. Nous allons à présent étendre les droits de ce dernier au moyen de policies supplémentaires.
Commençons par attribuer un rôle métier à l'utilisateur "user".
👉 Éditez le fichier tomcat-users.xml
(présent dans
C:\apps\apache-tomcat-9.0.76\conf\
si vous utilisez un environnement
similaire à celui mis en place dans la leçon "01 - intro").
👉 Ajoutez les rôles métiers suivants:
<role rolename="coupon-admin"/>
<role rolename="coupon-user"/>
Ici, nous avons créé deux rôles : le rôle coupon-admin
donnera accès aux
fonctions d'administration tandis que le rôle coupon-user
donnera accès
aux fonctionnalités standard.
👉 Attribuez ces rôles à l'utilisateur "user" en modifiant la ligne correspondant à ce "user" de la façon suivante:
<user username="user" password="1234" roles="EWT,coupon-admin,coupon-user"/>
Pour tester les différentes combinaisons de rôles, nous allons ajouter deux utilisateurs supplémentaires:
👉 Ajoutez les comptes suivants:
<user username="superuser" password="1234" roles="EWT,coupon-user,coupon-admin"/>
<user username="admin" password="1234" roles="EWT,coupon-admin"/>
À la fin des modifications, votre fichier tomcat-users.xml
devrait avoir
la forme suivante (les commentaires ont été retirés):
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Nous allons à présent définir les policies qui accordent les droits en fonction des nouveaux rôles.
👉 Créez un fichier admin.xml
dans le dossier policies
de l'application
et insérez-y le contenu suivant:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
👉 Créez un fichier user.xml
dans le dossier policies
de l'application
et insérez-y le contenu suivant:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
On remarquera au passage la différence de syntaxe utilisée pour énumérer les
actions entre les deux policies. Ewt autorise d'énumérer les actions (et les
permissions) en les plaçant dans une liste séparée par une virgule, en les
énumérant dans des balises <action>
séparées ou en faisant un mix des deux
formes. Vous pouvez choisir la forme qui vous convient le mieux.
Construction alternative
Ce paragraphe est optionnel. Nous y discutons d'une façon
alternative de construire la policy user
. Nous abordons quelques notions
utiles concernant les policies, mais nous aurons l'occasion d'y revenir
plus tard.
Dans le cas du rôle coupon-user
nous avons décidé d'énumérer toutes les
actions autorisées. On aurait également pu construire la policy
différemment, en accordant les droits sur toutes les actions, puis en
retirant les actions admin
et reset
ensuite. La policy aurait pu être
définie ainsi:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Attention, cette façon de procéder pour gérer les droits entraîne au moins deux conséquences:
- Une adaptation de la policy
admin
est nécessaire - Une référence de version doit être ajoutée à la policy
user
a) Adaptation de la policy admin
La policy admin
doit être modifiée ainsi :
1 2 3 4 5 6 7 8 9 10 11 12 |
|
La différence se situe au niveau de la balise <policy>
: nous avons
ajouté un attribut priority="1"
. Pourquoi ? Dans Ewt, les refus
priment sur les autorisations. Or, nous avons ici une policy admin
qui
accorde un droit sur les actions admin
et reset
et dans le même
temps une policy user
qui la refuse. Le refus l'emporte et par
conséquent les actions d'administration ne sont utilisables par personne.
L'attribut priority
permet de faire primer la policy admin
sur la
policy user
. La policy admin
déclare une priorité de 1, alors que la
policy user
n'en déclare aucune, ce qui revient à avoir une priorité
de 0. Une priorité plus haute l'emporte sur une priorité inférieure.
b) Verrouillage de version
Dans la policy user
ci-dessus, nous avons intégré une petite
différence qui qui a son importance du point de vue de la sécurité.
L'élément racine <policy>
a été complété avec un attribut
version="1.0"
. Cet attribut indique que l'on souhaite appliquer le
modèle de policy "1.0" dans ce cas. Il s'agit d'un garde-fou pour
éviter que l'entrée <action>*</action>
ne soit mal interprétée.
Explication
Comme indiqué au début de cette remarque, la policy ci-dessus accorde un
droit sur toutes les actions dans un premier temps, puis retire le droit
pour les actions admin
et reset
. L'interprétation de "toutes les
actions" peut varier en fonction de la version du moteur Ewt. Imaginons en
effet qu'une version future du moteur ajoute une nouvelle action avancée.
La policy ci-dessus accorderait de facto cette nouvelle action. Cela
pourrait représenter un risque de sécurité pour l'application.
Le fait de mentionner que la policy applique la version "1.0" du moteur
de policy fait que la référence wildcard <action>*</action>
représente
"toutes les actions connues de la version 1.0 du moteur de policy". Cela
évite qu'une action ajoutée dans la future version 1.1 ne soit prise en
compte. La documentation de référence relative aux policies sera
maintenue au fil des évolutions et indiquera à quelle version chaque
action est associée.
Conclusion
La méthode de définition par ajout/retrait de droits entraînent des
complications et des risques, raison pour laquelle elle n'est pas
recommandée. La solution consistant à procéder uniquement par ajout de
droit est donc à privilégier lorsque cela est possible.
Ces modifications étant faites, il faut à présent redémarrer le serveur
d'application Tomcat : cela permettra de recharger la configuration de
tomcat-users.xml
et effectuera dans le même temps un reset
de
l'application.
👉 Stoppez l'instance Tomcat puis démarrez-la à nouveau
À présent, le compte "user" devrait à nouveau pouvoir lancer des recherches,
créer et modifier des dossiers. Par contre il n'est pas autorisé à lancer
des reset
ou à accéder aux fonctions d'administration.
Le compte "admin" quant à lui devrait à l'inverse être autorisé sur les
actions d'administration et sur le reset
, mais ne devrait pas pouvoir
créer ou modifier de dossiers, ni lancer de recherche.
Enfin, le compte "superuser" devrait disposer de toutes les fonctionnalités.
Permission dépendant du statut¶
L'idée est de garder les champs en lecture/écriture tant qu'un coupon est actif et de rendre ces champs uniquement lisibles lorsque le coupon est inactif, à l'exception du champ "Statut" qui devra rester modifiable.
👉 Créez un fichier statut.xml
dans le dossier policies
de l'application
et ajoutez-y le contenu suivant:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Cette policy de ressource s'applique à tous les rôles utilisateur. Elle
retire le droit d'écriture et bloque certaines actions lorsque le champ
statut
d'un dossier a la valeur 0. La condition sur la valeur du champ est
écrite en script. Nous reviendrons plus tard sur le langage de script, mais
la syntaxe de la condition ci-dessus est assez simple à comprendre.
Cette policy de ressource a une priorité de 1 (cf. attribut priority="1"
).
La priorité permet de classer les policies par niveaux. Cela permet de
passer outre la règle voulant qu'une policy de type deny
prime sur une
policy de type allow
. Ce point est développé à l'aide d'un exemple dans le
bloc de remarque 2 plus haut dans cette leçon. Nous y revenons également
plus bas.
Au niveau du statement, nous avons spécifié un attribut
requireQualifiedContext="true"
. Il s'agit d'une optimisation et d'un
garde-fou pour éviter des erreurs inattendues. Il indique au moteur que le
statement n'est à traiter que lorsque la policy est évaluée dans un contexte
pleinement qualifié. L'idée est d'éviter d'évaluer le statement hors dossier,
car dans ce cas la résolution de #base.statut
ne pourrait pas se
faire et générerait une erreur.
Nous devons indiquer au moteur qu'il doit appliquer cette policy à tout le modèle "Coupon".
👉 Éditez le fichier descript.xml
et modifiez la définition du modèle
coupon
de façon à lui ajouter le bloc <policies>
comme ci-dessous:
1 2 3 4 5 6 7 |
|
👉 Effectuez un reset
de l'application au moyen d'un utilisateur disposant
du rôle coupon-admin
(donc admin
ou superuser
)
Une fois cette policy en place, tous les champs du dossier passent en lecture seule lorsque le statut est à 0, y compris le champ "Statut" lui-même. Ça peut être un problème si le changement de statut a été effectué par erreur. On va donc faire en sorte que le champ "Statut" reste modifiable dans tous les cas.
Il y a plusieurs façons de le faire. Dans cette leçon, nous proposons la solution suivante, mais elle n'est de loin pas la seule possible.
👉 Éditez le fichier descript.xml
et modifiez le champ statut
pour lui
donner la description suivante:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
En premier lieu, nous avons ajouté un élément policies
contenant une
description de policy "inline". En effet, Ewt permet de référencer par son
nom une policy définie dans le dossier policies
de l'application comme
nous l'avons fait pour la policy statut
, mais également directement dans la
description du champ. Ici nous optons pour cette variante car la policy que
nous définissons ne concerne que le champ "Statut" lui-même.
La structure de la policy en soi ne change pas par rapport à une policy
définie via un fichier. Ici nous indiquons une priorité 2 via l'attribut
priority="2"
. Cela est nécessaire pour que le allow
que nous définissons
ici prime sur le deny
de la policy statut
créée plus tôt.
Nous spécifions également un bloc <properties>
dans lequel nous déclarons
un <attribute>
. Les properties sont des sortes de fourre-tout. Elles
permettent à l'utilisateur d'y spécifier n'importe quel type de données. Ces
données seront reprises dans l'arbre de sortie et pourront être interprétées
par la feuille de style. L'élément <attribute>
n'est donc pas un mot-clé
reconnu par Ewt. Le moteur ne fait que de transmettre cet élément dans
l'arbre de sortie et c'est la feuille de style qui l'exploite à sa guise.
Dans le cas présent, la feuille de style reprend l'attribut en question en
tant qu'attribut de champ. Cela permet de forcer un rafraîchissement de
l'écran lorsque l'on change le statut de 0 à 1. Comme en mode "lecture
seule" l'interface n'affiche pas de bouton "Enregistrer", le code javascript
utilisé ici facilite l'enregistrement du changement de statut. Une
alternative aurait pu consister à rajouter un bouton "Rafraîchir" dans
l'interface, mais cela nécessite d'intervenir au niveau des styles et ce
n'est pas l'objet de la présente leçon.