Files
ObsiGate/docs/features/user-home-194.md
T
bruno 10453d8dfe
CI / lint (push) Successful in 3m1s
CI / security (push) Successful in 1m41s
CI / test (push) Successful in 4m37s
CI / build (push) Successful in 1m34s
CI / e2e (push) Successful in 17m40s
feat: dossier perso (#194) — vaults home-* en tête de l'arborescence, icône maison, compose de test
- 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).
2026-10-10 16:09:59 -04:00

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 :

  1. Un vault par utilisateur (retenu) — <racine>/<user> devient le vault home-<user>, octroyé dans user.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.
  2. Un vault Home unique + ACL par sous-dossier (écartée) — il n'existe aucune ACL par chemin dans le code ; il aurait fallu la brancher dans files_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 que CreateUserRequest.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'index vault::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 env VAULT_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ée vault_config[vault] après remove_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 par load_vault_config, release (dossier conservé), isolation check_vault_access entre 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 sans OBSIGATE_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 alimentent state.allVaults) → arborescence, sélecteurs et refreshs homogènes.
  • Icône : getVaultIcon() (frontend/js/viewer.js) renvoie icon("home", size) pour home-* → même pipeline lucide que le reste de l'app (safeCreateIcons, stroke: currentColor, suit le thème).
  • Test local : docker-compose.test-win.yml monte test_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-home rendu, 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é.