- 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).
3.8 KiB
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 :
- 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.
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é.