Journal d'audit¶
Le Journal d'audit, sous Admin → Paramètres → Journal d'audit (/admin/settings?tab=audit-log), répond aux deux questions qu'une revue de sécurité pose à une instance qui détient la carte de tout le paysage informatique : qu'est-ce qui y a changé, et qui s'est connecté, et comment. Il est divisé en deux sous-onglets :
| Sous-onglet | URL | Ce qu'il enregistre |
|---|---|---|
| Modifications | /admin/settings?tab=audit-log |
Chaque écriture dans le paysage — fiches, relations, risques, étiquettes, rôles de parties prenantes, brouillons de décision —, regroupée en lots de mutations, avec le diff de chaque événement et une annulation (Rollback) |
| Sécurité et administration | /admin/settings?tab=audit-log&view=security |
Connexions et déconnexions, tentatives échouées, verrouillages, sessions SSO et proxy, événements de mot de passe, emprunt de rôle et chaque action administrative |
Les deux sont séparés à dessein. Modifications est un outil de travail — il sert à voir et à annuler une écriture erronée — et ne conserve qu'un historique court. Sécurité et administration est une piste de preuves : ses entrées ne peuvent être ni modifiées ni supprimées, et elle est conservée un an par défaut.
Qui peut le consulter¶
Les deux sous-onglets exigent la permission admin.events (« Consulter la piste d'audit et le flux d'événements »), que seul le rôle Admin détient par défaut. Pour donner un accès en lecture à un responsable sécurité ou conformité sans en faire un administrateur, créez un rôle portant admin.events sous Utilisateurs et rôles et attribuez-le-lui.
Modifications¶
Le sous-onglet Modifications est le registre des lots de mutations. Chaque écriture qui modifie des données y arrive sous forme de lot, avec son auteur et sa provenance :
| Origine | Signification |
|---|---|
| Web | L'interface web de Turbo EA |
| API | Un appel direct à l'API REST (un script, une intégration) |
| Outil IA | Un assistant IA connecté via le serveur MCP |
| Extension | Une extension installée, enregistrée comme lot ext:<key> |
Cliquez sur un lot pour ouvrir son panneau : le diff de chaque événement montre l'ancienne et la nouvelle valeur de chaque champ, et Rollback rétablit les écritures sur les fiches, relations, risques, rôles de parties prenantes, étiquettes et brouillons de décision qu'il a effectuées.
Les lots sont conservés MUTATION_BATCH_RETENTION_DAYS jours (une variable d'environnement, 15 par défaut), puis purgés automatiquement. Les événements eux-mêmes restent dans l'onglet Historique de chaque fiche une fois le lot disparu.
Sécurité et administration¶

Le sous-onglet Sécurité et administration liste une entrée par action ayant trait à la sécurité, les plus récentes en premier :
- connexions et déconnexions, par mot de passe, par SSO ou via un proxy inverse de confiance ;
- tentatives de connexion échouées, chacune avec un code de motif indiquant pourquoi elle a échoué ;
- verrouillages de compte après des échecs répétés ;
- inscriptions, ainsi que les mots de passe définis, réinitialisés ou modifiés ;
- emprunt de rôle — un administrateur qui démarre puis arrête Voir en tant que rôle… ;
- les actions administratives — modifications des utilisateurs, des rôles, des paramètres, du métamodèle, des tags, des calculs, des extensions, des imports, des intégrations, des portails publics et des diagrammes publiés, des enquêtes et de l'accès opérateur (voir Actions administratives) ;
- les exports de ce journal lui-même.
Dans la prochaine version
Les exports de données (exports CSV/JSON de l'inventaire, exports de workspace et de migration) seront enregistrés dans ce même journal dans la prochaine version.
Colonnes¶
| Colonne | Description |
|---|---|
| Quand | Date et heure de l'action |
| Catégorie | auth (authentification), admin (actions administratives) ou access (exports et autres accès aux données) |
| Action | Ce qui s'est passé, par exemple auth.login.failed — voir le catalogue des actions |
| Résultat | Succès ou Échec |
| Acteur | L'utilisateur connecté qui a effectué l'action. Vide quand personne n'était connecté — lors d'une connexion échouée, par exemple |
| Sujet | Le compte concerné par l'action — l'adresse e-mail saisie lors d'une connexion échouée, ou l'utilisateur dont le mot de passe a été réinitialisé |
| Cible | L'objet visé, s'il y en a un — par exemple le rôle choisi au démarrage d'un emprunt de rôle |
| IP | L'adresse IP du client (voir Adresse IP du client) |
| Origine | La provenance de la requête : web, API, outil IA, extension, ops ou système |
| Rôle emprunté | Quand un administrateur consultait l'application sous un autre rôle, ce rôle. L'acteur reste l'administrateur réel |
Les colonnes s'affichent, se masquent, se réordonnent et se figent comme dans tout autre tableau de données ; Quand et Action sont toujours affichées.
Filtres¶
Le panneau de filtres à gauche restreint la liste par catégorie, résultat, action, acteur, adresse IP et plage de dates. Chaque en-tête de section indique le nombre de valeurs sélectionnées, et le panneau replié affiche le nombre de filtres actifs. L'onglet Colonnes du même panneau affiche, masque et fige les colonnes. Vos filtres et la disposition des colonnes sont mémorisés dans ce navigateur.
Une recherche sur une adresse e-mail porte sur l'acteur, le sujet et la cible : saisir une seule adresse retrouve à la fois ce que cette personne a fait et ce qui a été tenté contre son compte.
Détails d'une entrée¶
Cliquez sur une ligne pour ouvrir le panneau de l'entrée. Il affiche chaque champ, y compris l'agent utilisateur du navigateur et les détails enregistrés avec l'entrée — le code de motif d'une connexion échouée, l'heure de fin d'un verrouillage, le fournisseur d'identité d'une connexion SSO. Lorsqu'une entrée enregistre une modification, les détails s'affichent sous forme de tableau des anciennes et nouvelles valeurs. Les secrets ne sont jamais stockés : un mot de passe, un jeton ou un secret client est remplacé par *** avant l'écriture de l'entrée.
Les éléments qui situent une modification sans en faire partie — l'adresse actuelle d'un portail et s'il est publié, le remplacement ou non des identifiants d'une connexion, les types couverts par un mappage de migration — sont listés sous Contexte, sous le tableau.
Le panneau ne propose aucune action. Les entrées ne peuvent être ni modifiées ni supprimées.
Exporter en CSV¶
Exporter en CSV, dans la barre d'outils, télécharge les entrées correspondant aux filtres actuels sous forme de fichier CSV, jusqu'à 50 000 lignes. L'export est lui-même enregistré, sous access.audit_log.exported, si bien qu'un export du journal de sécurité y laisse toujours une trace.
Catalogue des actions¶
| Action | Enregistrée lorsque |
|---|---|
auth.login.succeeded |
un utilisateur se connecte avec son e-mail et son mot de passe |
auth.login.failed |
une connexion par mot de passe est refusée. Le code de motif indique pourquoi (voir ci-dessous) |
auth.login.locked |
la cinquième tentative échouée d'affilée verrouille le compte pendant 15 minutes. Les détails indiquent l'heure de fin du verrouillage |
auth.logout |
un utilisateur connecté se déconnecte. Une déconnexion sans session valide n'enregistre rien |
auth.sso.succeeded |
un utilisateur se connecte via un fournisseur SSO. Les détails indiquent le fournisseur, le sujet du fournisseur d'identité et si le compte a été créé par cette connexion |
auth.sso.failed |
une connexion SSO est refusée — domaine non autorisé, adresse e-mail manquante, compte local avec le même e-mail, compte inactif. Les détails indiquent le fournisseur et le motif |
auth.proxy_session.succeeded |
un utilisateur se connecte via un proxy inverse de confiance |
auth.proxy_session.failed |
une connexion via le proxy inverse est refusée |
auth.register |
un utilisateur crée un compte par auto-inscription |
auth.password.set |
un utilisateur invité définit son premier mot de passe |
auth.password.reset_requested |
Quelqu'un demande un e-mail de réinitialisation du mot de passe. Toujours enregistré comme un succès avec l'adresse saisie, pour que la réponse ne révèle rien ; les détails indiquent si un compte correspondait et si un e-mail a été envoyé |
auth.password.reset |
un mot de passe est modifié via un lien de réinitialisation |
auth.password.changed |
un utilisateur connecté modifie son propre mot de passe |
auth.impersonation.started |
un administrateur démarre Voir en tant que rôle…. La cible est le rôle choisi |
auth.impersonation.stopped |
l'administrateur revient à son propre rôle |
access.audit_log.exported |
quelqu'un exporte le journal de sécurité en CSV |
Codes de motif de auth.login.failed :
| Motif | Signification |
|---|---|
unknown_user |
Aucun compte ne porte cette adresse e-mail |
sso_account |
Le compte se connecte par SSO et n'a pas de mot de passe |
no_password |
Le compte n'a pas encore de mot de passe — une invitation jamais acceptée |
locked |
Le compte est verrouillé après des échecs répétés |
bad_password |
Le mot de passe est erroné |
inactive |
Le compte est désactivé |
Codes de motif de auth.sso.failed, auth.proxy_session.failed, auth.password.set et auth.password.reset :
| Motif | Signification |
|---|---|
expired |
L'accès limité dans le temps du compte a pris fin |
hd_mismatch |
Le compte Google appartient à un domaine non autorisé |
no_email |
Le fournisseur d'identité n'a renvoyé aucune adresse e-mail |
no_subject |
Le fournisseur d'identité n'a renvoyé aucun identifiant de sujet |
local_account |
Un compte local avec mot de passe utilise déjà cette adresse e-mail |
no_account |
Aucun compte n'existe et les comptes ne sont pas créés automatiquement |
exchange_failed |
Le fournisseur d'identité n'a pas terminé la connexion |
unauthenticated |
Le proxy inverse n'a pas authentifié la requête |
identity_rejected |
L'identité transmise a été refusée (domaine ou e-mail non vérifié) |
invalid_token |
Le lien d'invitation ou de réinitialisation était invalide ou a expiré |
Une rafale d'échecs unknown_user depuis une même adresse IP est la signature d'une pulvérisation de mots de passe (password spraying) ; une série de bad_password contre un compte suivie de auth.login.locked est une tentative de devinette contre ce compte.
Actions administratives¶
Chaque modification qu'un administrateur apporte à la configuration de l'instance est enregistrée dans la catégorie admin. L'acteur est l'administrateur, la cible est l'objet modifié — un utilisateur, un rôle, un paramètre, un type de fiche — avec son nom tel qu'il était à ce moment-là, et les détails indiquent ce qui a changé sous forme d'anciennes et de nouvelles valeurs, limitées aux champs réellement modifiés. Deux règles s'appliquent à chaque entrée :
- Les secrets sont masqués. Un mot de passe SMTP, un secret client SSO, une clé de fournisseur d'IA, un identifiant ServiceNow ou toute autre valeur secrète est écrit
***: l'entrée montre qu'il a changé, jamais ce qu'il était ni ce qu'il est devenu. - Aucun texte de licence ni identifiant n'est jamais stocké. Une licence d'extension est identifiée, pas copiée, et un mot de passe qu'un administrateur définit pour un utilisateur est enregistré comme modifié, jamais avec sa valeur.
Un enregistrement qui ne change rien n'est pas consigné, pas plus qu'un utilisateur qui modifie son propre nom ou sa langue dans son profil. Une configuration volumineuse — les champs, traductions ou la mise en page d'un type de fiche, les filtres d'un portail — est résumée par sa taille plutôt que copiée ; lorsque la taille n'a pas changé, une courte empreinte du contenu est ajoutée, afin que l'entrée montre malgré tout qu'il y a eu une modification. Les surcharges de permissions par rôle d'un type de fiche font exception : chaque cellule modifiée est listée avec son ancienne et sa nouvelle valeur, une valeur vide signifiant que la permission propre du rôle s'applique.
| Domaine | Actions | Ce qui est enregistré |
|---|---|---|
| Utilisateurs et rôles | admin.user.created, .updated, .deleted, .bulk_updated, .bulk_deleted, .unlocked; admin.user.invitation.resent, .deleted; admin.role.created, .updated, .archived, .restored; admin.stakeholder_role.created, .updated, .deleted, .archived, .restored |
Comptes créés, modifiés, activés ou désactivés, supprimés (un par un ou en masse) et déverrouillés, avec les champs modifiés — rôle, e-mail, nom, état actif ; invitations renvoyées ou révoquées ; rôles applicatifs et rôles de parties prenantes par type, avec les permissions accordées ou retirées |
| Paramètres | admin.settings.updated |
Une entrée par paramètre enregistré — e-mail, authentification et SSO, inscription, IA, MCP, langues, activation des modules, image de marque, logo et favicon, durées de conservation. La cible est le paramètre ; les détails sont son ancienne et sa nouvelle valeur, secrets masqués |
| Métamodèle | admin.metamodel.type.* (including .references_generated), admin.metamodel.relation_type.*, admin.metamodel.resource_type.*, admin.principle.* (including .imported), admin.regulation.* |
Types de fiches, types de relations et types de ressources créés, modifiés, supprimés ou restaurés, avec les propriétés modifiées ; références de fiches générées pour un type ; principes EA, y compris ceux importés depuis le catalogue ; réglementations de conformité |
| Tags et calculs | admin.tag_group.*, admin.tag.*, admin.calculation.* |
Groupes de tags et tags ; champs calculés créés, modifiés, supprimés, activés ou désactivés, avec l'ancienne et la nouvelle formule, et l'exécution d'un calcul sur toutes les fiches (.recalculated) |
| Extensions | admin.extension.install.uploaded, .applied, .discarded; admin.extension.store.claimed, .refreshed, .installed; admin.extension.enabled, .disabled, .uninstalled; admin.extension.license.updated, .removed |
Paquets téléversés, installés ou abandonnés ; licences et téléchargements depuis la boutique d'extensions ; extensions activées, désactivées et désinstallées ; licence remplacée ou supprimée — avec l'extension et sa version, jamais le texte de la licence |
| Transfert de workspace et migration | admin.workspace.import.uploaded, .applied, .completed, .discarded; admin.migration.uploaded, .field_mappings.updated, .applied, .deleted |
Un paquet de workspace téléversé, son import lancé, terminé ou abandonné ; une migration de plateforme téléversée, ses correspondances de champs modifiées, appliquée ou supprimée |
| Intégrations | admin.servicenow.connection.*, admin.servicenow.mapping.*, admin.servicenow.sync.pulled, .pushed, .applied |
Connexions ServiceNow, identifiants masqués, et correspondances ; chaque récupération, envoi et application d'enregistrements en attente |
| Exposition publique | admin.web_portal.created, .updated, .deleted; admin.diagram.published, .unpublished |
Portails web et diagrammes publiés — tout ce qui rend des données accessibles sans compte |
| Enquêtes | admin.survey.sent, admin.survey.closed |
Une enquête envoyée à ses destinataires, ou clôturée |
| Accès opérateur | admin.ops.rescue_access.granted, .revoked |
Accès de secours accordé ou révoqué via l'API d'exploitation, enregistré à côté des événements ops.* existants |
Ce qui n'est pas enregistré¶
- Le renouvellement du jeton. L'application web renouvelle sa session à intervalle régulier : chaque onglet ouvert ajouterait des entrées, sans qu'aucune ne signifie quoi que ce soit.
- Les requêtes refusées pour un jeton de session expiré ou invalide. Elles se produisent chaque fois qu'une session se termine normalement.
- Les lectures de flux OData. Excel et Power BI interrogent le flux OData d'une vue sauvegardée selon un calendrier ; chaque actualisation ajouterait une entrée.
- Le simple accès en lecture — ouvrir une fiche, exécuter un rapport, rechercher dans l'inventaire.
Conservation¶
Les entrées sont conservées 365 jours par défaut. Modifiez cette durée sous Admin → Paramètres → Général → Gestion des données → Conservation du journal de sécurité : n'importe quel nombre de jours jusqu'à 3650, ou 0 pour conserver les entrées indéfiniment. Une tâche supprime chaque nuit les entrées plus anciennes que cette durée et relit le paramètre à chaque passage, de sorte qu'un changement prend effet sans redémarrage.
Cette tâche de conservation est la seule chose qui supprime jamais une entrée. Il n'existe aucune action de modification ou de suppression, ni dans l'interface ni dans l'API, pour aucun rôle.
Le journal ne fait volontairement pas partie du Transfert de workspace : il constitue l'historique de sécurité de cette instance, pas le contenu du workspace, et cloner une instance ne le copie pas.
Le flux d'audit JSON¶
Chaque entrée est aussi écrite par le backend sous forme d'une ligne JSON sur le logger turbo_ea.audit, au moment même où elle est enregistrée. Le backend journalise sur la sortie standard, si bien que le flux parvient à tout ce qui collecte déjà les journaux de vos conteneurs — docker compose logs backend, la collecte de journaux de Kubernetes ou n'importe quel agent d'expédition vers un SIEM —, sans aucune configuration dans Turbo EA :
docker compose logs -f backend | grep turbo_ea.audit
La ligne est écrite au moment où l'entrée est enregistrée. Si la transaction de la requête échoue ensuite, la ligne existe sans enregistrement en base : la table fait foi, le flux est le fil en direct.
Chaque ligne porte les mêmes champs que l'entrée :
| Champ | Contenu |
|---|---|
audit_id, created_at |
L'identifiant de l'entrée et le moment où elle a été écrite |
category, action, outcome |
Ce qui s'est passé et si cela a réussi |
actor_user_id, actor_email |
Qui l'a fait |
subject_email |
Le compte concerné |
target_type, target_id, target_label |
Ce sur quoi l'action a porté |
ip, user_agent |
Le client |
origin |
web, api, mcp, ext, ops ou system |
impersonator_user_id, impersonated_role |
Renseignés pendant qu'un administrateur consulte l'application sous un autre rôle |
details |
Codes de motif et anciennes/nouvelles valeurs, secrets déjà masqués |
Expédier ce flux vers un stockage central de journaux en conserve une copie hors de l'instance, hors de portée de quiconque peut l'administrer — c'est ce dont a besoin une revue des menaces internes. Voir Journalisation et flux d'audit pour le collecter.
Adresse IP du client¶
La colonne IP enregistre l'adresse du client, et non celle du proxy placé devant le backend. Le nginx de bordure fourni renseigne l'en-tête X-Real-IP avec l'adresse d'où il a reçu la connexion, et le backend lit cet en-tête parce que l'image backend publiée définit TRUST_PROXY_HEADERS=true. Une valeur qui n'est pas une adresse IP valide est écartée plutôt qu'enregistrée.
- Laissez
TRUST_PROXY_HEADERSactivé tant que chaque requête atteint le backend via le nginx fourni — la pile par défaut, le chart Helm et les modèles de conteneurs managés fonctionnent tous ainsi. - Désactivez-le (
TRUST_PROXY_HEADERS=false) si vous exposez directement le conteneur backend, sans le nginx fourni devant lui : un client pourrait alors envoyer son propreX-Real-IPet choisir l'adresse enregistrée. - Derrière un autre répartiteur de charge, le nginx fourni voit l'adresse de ce répartiteur, et chaque entrée l'enregistre. Un paramètre permettant à nginx de faire confiance à l'adresse client transmise par votre répartiteur de charge (
NGINX_TRUSTED_PROXIES) est prévu dans une version ultérieure.