Aller au contenu principal

Piste d'audit

Le besoin​

Qui a créé cette ligne, qui l'a modifiée en dernier, et quel était son contenu auparavant ? La réponse ne doit pas dépendre de ce qu'envoie le navigateur.

La démarche​

Deux niveaux, qui se combinent :

NiveauMécanismeRésultat
Dernière modification, sur la ligneJetons {{LOGIN}} / {{SYSTIMESTAMP}} dans les requêtes d'écriture.CREATED_BY, CREATED_AT, UPDATED_BY, UPDATED_AT renseignés par le serveur.
Chaque écriture, dans une table d'historiqueLe réglage audit_table de l'écran.Une ligne par insertion, mise à jour ou suppression, avec l'action, l'utilisateur et la date.

La recette​

1. Colonnes d'audit sur la table​

ALTER TABLE customers ADD created_by VARCHAR(64);
ALTER TABLE customers ADD created_at TIMESTAMP;
ALTER TABLE customers ADD updated_by VARCHAR(64);
ALTER TABLE customers ADD updated_at TIMESTAMP;

2. Les renseigner dans les requêtes d'écriture​

Dans Liberty Next → Construire → Connecteurs, modifier les requêtes d'insertion et de mise à jour de la table :

-- customers_post
INSERT INTO customers (ID, NAME, STATUS, CREATED_BY, CREATED_AT)
VALUES (:ID, :NAME, :STATUS, {{LOGIN}}, {{SYSTIMESTAMP}})

-- customers_put
UPDATE customers
SET NAME = :NAME, STATUS = :STATUS,
UPDATED_BY = {{LOGIN}}, UPDATED_AT = {{SYSTIMESTAMP}}
WHERE ID = :ID_ORIGINAL

Le serveur remplace les jetons à l'exécution — nom de l'utilisateur connecté en majuscules, date et heure courantes — quelles que soient les valeurs envoyées par le formulaire. La requête de mise à jour ne touche pas à CREATED_BY / CREATED_AT, qui conservent leur valeur.

Règles du dictionnaire plutôt que jetons

Les règles du dictionnaire LOGIN et SYSDATE font de même pour toute écriture qui lie la colonne — mais LOGIN réécrit la colonne à chaque écriture, mises à jour comprises, et SYSDATE ne complète qu'une valeur vide. Elles conviennent à UPDATED_BY sur une requête qui le lie ; pour les colonnes de création, les jetons dans la requête d'insertion sont le choix le plus sûr.

3. Afficher les colonnes​

Sur l'écran, donner aux quatre colonnes un libellé (entrées du dictionnaire) et un format date-heure, cocher disabled (lecture seule) et les placer dans un onglet Historique du dialogue — ou les laisser hors du dialogue et ne les afficher que dans la grille.

4. Conserver chaque écriture — audit_table​

Dans l'onglet Général de l'écran, renseigner audit_table avec AUD_CUSTOMERS. Dès lors, chaque insertion, mise à jour ou suppression faite depuis l'écran écrit aussi une ligne dans AUD_CUSTOMERS :

ColonneContenu
Les colonnes de la tableLes valeurs envoyées avec l'écriture.
AUD_ACTIONINSERT, UPDATE ou DELETE.
AUD_USERLe nom de l'utilisateur.
AUD_DATELa date et l'heure du serveur.

La table est créée à la première écriture, à partir de la structure de la table source. Plusieurs écrans peuvent partager la même table d'audit.

Pourquoi c'est sûr​

TentativeRésultat
Modifier le formulaire pour envoyer UPDATED_BY = 'quelqu'un d'autre'.Le jeton est résolu par le serveur ; la valeur du formulaire n'est pas utilisée.
Appeler directement l'API REST avec un contenu forgé.Même chose : jetons et règles sont appliqués par le serveur, après lecture de la requête.
Écrire sans être connecté.L'écriture exige un utilisateur authentifié disposant de la permission de la requête.

Variantes​

BesoinMoyen
Un numéro de versionSET VERSION = VERSION + 1 dans la requête de mise à jour.
L'historique de toute une applicationUne table d'audit commune à ses écrans (AUD_SECURITY).
Des modifications livrées d'un environnement à l'autreLe suivi des modifications sur l'écran — voir Paquets de modifications.

Pour aller plus loin​