- frontend/js/sidebar.js : tri stable _homeFirst appliqué à la source des deux listes de vaults (loadVaults + soft refresh) → les vaults home-<user> passent en premier, arborescence et sélecteurs cohérents. - frontend/js/viewer.js : getVaultIcon renvoie l'icône lucide "home" pour home-* (même safeCreateIcons / stroke currentColor que les autres). - docker-compose.test-win.yml : montage test_home:/vaults/Home + OBSIGATE_HOME_ROOT=/vaults/Home pour tester la feature en local ; test_home/ ajouté au .gitignore. - Tests : sidebar-filters.test.mjs +2 (icône maison, gemme violette inchangée), vérifié live sur l'instance 2020 (ordre + svg.lucide-home).
4.6 KiB
4.6 KiB
#194 — Dossier personnel par utilisateur
Statut : livré | Ouvert : 2026-10-09 | Type : feature (auth + vaults)
Problème
Un utilisateur créé dans ObsiGate n'avait aucun espace propre : il ne voyait que les vaults qu'un admin lui attribuait à la main, et rien n'était préparé sur le disque à sa création.
Décision d'architecture
Deux formes étaient possibles :
- Un vault par utilisateur (retenu) —
<racine>/<user>devient le vaulthome-<user>, octroyé dansuser.vaults. L'ACL par vault existante (backend/auth/middleware.py::check_vault_access, appliquée partout : API, MCP, search, push, tools) fait l'isolation sans rien inventer. - Un vault
Homeunique + ACL par sous-dossier (écartée) — il n'existe aucune ACL par chemin dans le code ; il aurait fallu la brancher dansfiles_read,files_media,history,backups,mcp/tools,push… Surface de sécurité trop grande pour un gain marginal.
Décisions produit validées : nom home-<user>, dossier conservé à la
suppression du compte, racine /NFS/OBSIDIAN_DOC/Home montée /vaults/Home.
Mise en place
| Élément | Où |
|---|---|
Service idempotent ensure_user_home() / release_user_home() / ensure_all_user_homes() |
backend/user_home.py |
Registre persistant des vaults dynamiques data/vaults.json, fusionné par load_vault_config() |
backend/indexer.py (persist_vault / unpersist_vault) |
| Appel à la création et à la suppression d'un compte | backend/auth/router.py (POST/DELETE /api/auth/admin/users) |
| Passe de réparation/migration au démarrage | backend/main.py (lifespan → après le watcher) |
Persistance des vaults ajoutés par /api/vaults/add + retrait |
backend/routers/vaults.py |
| Montage + variable d'environnement | docker-compose.yml (/NFS/OBSIDIAN_DOC/Home:/vaults/Home, OBSIGATE_HOME_ROOT=/vaults/Home) |
Garde-fous
- Fonctionnalité inactive sans
OBSIGATE_HOME_ROOT: dev, tests et desktop n'ont aucun comportement modifié. - Validation du nom :
^[a-zA-Z0-9_-]{2,32}$(même grammaire queCreateUserRequest.username_valid), re-vérifiée avant de construire un chemin — défense en profondeur contre le path traversal. - Nom de vault sans
/ni::: le nom sert de segment d'URL (/api/file/{vault}/…) et de clé d'indexvault::path. - Auto-réparation :
ensure_all_user_homes()au démarrage crée les dossiers manquants, ré-enregistre les vaults perdus et octroie l'accès aux users créés hors API. Toute erreur disque (NFS indisponible) est journalisée et rattrapée au boot. - Suppression de compte : index/watcher/registre refermés, dossier sur disque conservé.
Bug corrigé au passage
load_vault_config()ne lisait que les envVAULT_N_*/DIR_N_*: tout vault ajouté à la volée (y compris via/api/vaults/add) disparaissait au premier reindex complet et au redémarrage. →data/vaults.json.reload_single_vault()perdait l'entréevault_config[vault]aprèsremove_vault_from_index().
Tests
tests/test_user_home.py(10) : création (dossier + vault + octroi + registre), idempotence, username invalide, désactivation sans variable, relecture du registre parload_vault_config,release(dossier conservé), isolationcheck_vault_accessentre users, passe de réparation.tests/test_auth_api.py::TestUserHome(2) : provisionnement via l'API (création → dossier, vault visible, alice ne voit que son home, suppression → vault refermé / dossier conservé) + absence de dossier sansOBSIGATE_HOME_ROOT.
UI — arborescence (incrément)
- Ordre : tri stable
home-*en premier, appliqué à la source (frontend/js/sidebar.js::_homeFirst, sur les deux sites qui alimententstate.allVaults) → arborescence, sélecteurs et refreshs homogènes. - Icône :
getVaultIcon()(frontend/js/viewer.js) renvoieicon("home", size)pourhome-*→ même pipeline lucide que le reste de l'app (safeCreateIcons,stroke: currentColor, suit le thème). - Test local :
docker-compose.test-win.ymlmontetest_home:/vaults/Home+OBSIGATE_HOME_ROOT=/vaults/Home(test_home/est dans.gitignore). - Vérifié live (instance 2020, Playwright) : ordre
home-admin, home-bruno, TestDir, TestVault,svg.lucide-homerendu, placeholder<i>remplacé.
Impact utilisateur
Aucune nouvelle chaîne i18n : les noms de vault sont des données, la liste
provient déjà de GET /api/vaults. Le dossier apparaît tout seul dans le
sélecteur de vaults de l'utilisateur concerné.