Files
ObsiGate/docs/features/user-home-194.md
T
bruno 8235d632b8
CI / lint (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / security (push) Canceled after 0s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
feat: dossier personnel par utilisateur — vault home-<user> + registre de vaults persistant #194
- backend/user_home.py : ensure_user_home()/release_user_home() idempotents
  (mkdir, vault, watcher, octroi dans user.vaults), rappelés à la création
  d'un compte et au démarrage (migration + réparation + sweep des vaults
  home-* orphelins), dossier conservé à la suppression.
- backend/indexer.py : data/vaults.json, registre persistant des vaults
  ajoutés à la volée, fusionné par load_vault_config() — ils disparaissaient
  au premier reindex complet et au redémarrage. reload_single_vault remet
  vault_config[name] après remove_vault_from_index.
- backend/auth/router.py + routers/vaults.py : appels ensure/release +
  persistance à l'ajout/retrait d'un vault.
- docker-compose.yml : montage unique /NFS/OBSIDIAN_DOC/Home:/vaults/Home +
  OBSIGATE_HOME_ROOT (absente = fonctionnalité inactive).
- Tests : tests/test_user_home.py (12) + tests/test_auth_api.py::TestUserHome (2).
2026-10-10 00:40:35 -04:00

3.8 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.

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é.