v1.7.21
Retour à l'indexVersion précédente publiée : 1.7.20 (1 octobre 2026)
Backend : .NET 10 + IIS / Linux nginx
Frontend : Angular 21
Version consacrée à la sécurité et à l'accès aux données. Elle clôt les constats d'une revue de sécurité complète du framework et amène OData au même niveau de permissions que le CRUD. En résumé :
enableCookieAuthenticationaccepte une troisième valeur,"signed": cookie signé par le serveur, plusieurs sessions par utilisateur, déconnexion par session ;- OData applique les permissions de route et de colonne, les contraintes de ligne de l'utilisateur, et écrit en passant par le CRUD du framework ;
- les jetons personnels permettent de connecter Power BI, Excel et des scripts aux endpoints OData ;
- l'option « secure upload » des colonnes d'upload protège de nouveau les fichiers de la colonne ;
- la modification et la suppression respectent la restriction par utilisateur aussi dans le CRUD de l'interface ;
- les erreurs affichent à l'utilisateur un message traduit avec un code de suivi, les détails techniques seulement au superadmin.
Les modifications du schéma des métadonnées s'appliquent seules au démarrage. Les fonctionnalités ont été vérifiées par les tests end-to-end sur SQL Server, MySQL, PostgreSQL et Oracle, dans les trois modes de enableCookieAuthentication.
🔐 Authentification et sessions
- Trois modes.
enableCookieAuthenticationvautfalse,trueou"signed". Avecfalsel'utilisateur est celui que déclare le cookie du navigateur : réservé au développement. Au démarrage un message[security]le signale, l'éditeur AppSettings le décrit et demande confirmation à l'enregistrement. Une valeur non reconnue vaut"signed", avec un avertissement dans le log. "signed". Le serveur signe le cookiek-user(HMAC-SHA256, expiration dans la signature) : un cookie modifié ou expiré vaut comme absent. Le même utilisateur peut avoir plusieurs sessions ouvertes, chacune avec son identifiant.- Déconnexion et révocation. La déconnexion ferme uniquement la session d'où elle part, copies du cookie comprises. La déconnexion d'un utilisateur par un administrateur, la réinitialisation et le changement du mot de passe ferment toutes les sessions de cet utilisateur dans sa société.
- Durée maximale. Avec
signedSessionMaxLifetimeHours(24 par défaut,0= sans limite) une session signée expire à cet âge depuis la connexion, même utilisée en continu. - Clé de signature.
cookie-signing-keyest générée au premier démarrage et enregistrée dansappsettings.json. Au démarrage le log affiche l'empreinte de la clé (jamais la clé) et avertit si la clé n'a pas été enregistrée. - Appels vérifiés sur le serveur. Avec
falsele cookie ne porte plus de privilèges forgés (administrateur, rôle, société) : l'utilisateur est relu depuis la base de données.
🔗 OData
- Permissions. Chaque entity set exige la permission de lecture sur la route. Une colonne refusée à l'utilisateur et citée dans
$select,$filter,$orderbyou$expandrépond 403 ; sinon elle revient vide. - Contraintes de ligne. La requête part du même SELECT que le CRUD avec la restriction par utilisateur, rôle ou société, le filtre par défaut et la suppression logique.
$filter,$orderby,$skip,$topet$counts'appliquent à l'extérieur et peuvent seulement restreindre :or 1 eq 1n'élargit pas le résultat. $expand. Admis vers des routes lisibles sans contraintes de ligne ni suppression logique ; 403 dans les autres cas.- Écritures.
POST,PATCHetDELETEpassent parinsertRecord,updateRecordetdeleteRecord: permissions, colonnes non modifiables, triggers, champs de journalisation, change log, workflows et suppression logique s'appliquent comme depuis l'interface. Une ligne cachée à l'utilisateur répond 404. - Multi-société. La connexion aux données est celle de la société de l'utilisateur, comme dans le CRUD.
- Découverte.
/odata,/odata/$metadataet/odata/openapi.jsonexigent une session. AvecodataPublicMetadata=trueils redeviennent publics ; les données restent protégées. - Jetons personnels. Avec
apiTokensEnabled=truechaque utilisateur crée depuis le menu utilisateur (« Jetons API ») des jetonswuic_pat_…avec un nom, une expiration (90 jours par défaut, maximumapiTokenMaxLifetimeDays) et une permission en lecture seule ou aussi en écriture. Ils ne valent que sur/odata, avec les permissions du propriétaire. Power BI et Excel les utilisent comme mot de passe avec des identifiants « Basic » ; les scripts avecAuthorization: Bearer. Le jeton n'est affiché qu'une fois ; le superadmin voit et révoque ceux de tous.
🛡️ Sécurité
Hardening best-effort sur toute la surface : contrôles des valeurs qui finissent dans les requêtes (tris, opérateurs, agrégats, clés, filtres numériques) alignés sur les quatre fournisseurs ; chemins d'upload et de rapports confinés dans leurs dossiers ; fonctions d'administration (designer de rapports, suppression et scaffolding des rapports, réordonnancement des colonnes, redémarrage, scaffold OData) réservées au superadmin vérifié en base ; e-mails des workflows seulement par qui peut exécuter le workflow ; données d'exemple des outils pour assistants IA seulement au superadmin et jamais avec des colonnes d'identifiants ; gardes sur les utilisateurs et les rôles liées à la table, pas au nom de la route ; clé publique de licence intégrée au paquet.
📎 Upload
- « Secure upload » par colonne. Les fichiers d'une colonne avec
upload_securene se lisent qu'avec une session valide et la colonne visible pour l'utilisateur, depuis/uploadcomme depuis/api/UploadImage. Les fichiers des autres colonnes restent publics. Les colonnes qui enregistrent le fichier dans la base de données sont protégées par/api/UploadImage. - Noms de fichier. Les noms avec des points internes (
facture-acme.com.pdf) sont acceptés ; les noms avec des extensions exécutables par le serveur restent refusés même au milieu (photo.aspx.png).
🚦 Erreurs
L'utilisateur reçoit un message traduit avec un code de suivi (bouton « Copier le code » dans le dialogue d'erreur). Le même code est enregistré avec la pile complète dans _error__logs. SQL, piles et messages internes n'arrivent qu'au superadmin. Les traductions des nouveaux messages d'erreur s'ajoutent seules au démarrage.
🐛 Corrections notables
- Restriction par utilisateur en modification et suppression. L'UPDATE et le DELETE du CRUD ne contenaient que la clé : en connaissant l'id, un utilisateur pouvait modifier la ligne d'un autre. Désormais la ligne doit faire partie de celles que l'utilisateur voit, sinon 403
errors.auth.route_read_forbidden. - Filtre par défaut avec des filtres en OR. Avec l'opérateur OR le filtre par défaut de la route disparaissait et la grille montrait les lignes qu'elle devait cacher. Il reste désormais en AND.
- Oracle. Booléens écrits en 1/0 aussi sur les colonnes de type UI booléen, dans les filtres et dans les paramètres des procédures stockées ; fonctions distinguées des procédures dans le bon schéma ; OData sur le schéma des données même avec l'utilisateur
SYSTEM(avant, erreurs ORA-00942 intermittentes). - PostgreSQL et Oracle. L'insertion avec une clé identity non envoyée par le client n'échoue plus ; les rapports appliquent les permissions et les filtres de l'utilisateur comme sur SQL Server et MySQL.
- Premier démarrage sous IIS. La configuration initiale ne renvoie plus de 500 après une installation réussie : les migrations se terminent au redémarrage du worker.
- Spreadsheet et synchronisation. Avec un filtre actif, la modification et la suppression touchaient la mauvaise ligne ; une sélection au-delà de la page n'atteint plus les enregistrements des pages suivantes ; l'enregistrement par lot du kanban et la synchronisation hors ligne ne réessaient que ce qui a échoué.
- Menu. La barre apparaît avec les libellés déjà traduits, sans afficher un instant les clés.
📦 Paquets mis à jour
| Paquet | De | À |
|---|---|---|
WuicCore |
1.7.20 | 1.7.21 |
Wuic.Webcore |
1.7.20 | 1.7.21 |
WuicOData |
1.7.20 | 1.7.21 |
RuntimeEfCore |
1.7.20 | 1.7.21 |
Wuic.MySqlProvider |
1.7.20 | 1.7.21 |
Wuic.PostgresProvider |
1.7.20 | 1.7.21 |
Wuic.OracleProvider |
1.7.20 | 1.7.21 |
wuic-framework-lib (npm) |
1.7.20 | 1.7.21 |
🔧 Mises à jour opérationnelles recommandées
- Mode du cookie : en production, régler
enableCookieAuthenticationsur"signed"(outrue) ;falseest réservé au développement. - Plusieurs instances derrière un répartiteur de charge : copier la même
cookie-signing-keysur toutes les instances ; avec des clés différentes, une session ne vaut que sur l'instance qui l'a créée. Vérifier dans le log que l'empreinte coïncide. - Clients OData : les clients qui lisaient
$metadatasans session doivent s'authentifier, ou réglerodataPublicMetadata=true. Les écritures OData exécutent désormais les triggers et la journalisation du CRUD et, sur les routes avec suppression logique, positionnent l'indicateur au lieu de supprimer la ligne. md_service_apply_default_filter: obsolète. Le filtre par défaut s'applique toujours, OData compris.- Licence : la surcharge de l'empreinte de la machine se règle uniquement avec la variable d'environnement
WUIC_LICENSE_MACHINE_FINGERPRINT; les cléslicense-machine-fingerprint-overrideetlicense-public-key-pemdansappsettings.jsonsont ignorées. - Jetons API : pour connecter Power BI, Excel ou des scripts, régler
apiTokensEnabled=true(et éventuellementapiTokenMaxLifetimeDays) depuis l'éditeur AppSettings.