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).
This commit is contained in:
+60
-1
@@ -481,4 +481,63 @@ class TestSecureCookies:
|
||||
assert "secure" in resp.headers.get("set-cookie", "").lower()
|
||||
finally:
|
||||
if hasattr(https_client, "close"):
|
||||
https_client.close()
|
||||
https_client.close()
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
# Dossier personnel par utilisateur (#194)
|
||||
# ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
class TestUserHome:
|
||||
def _login(self, client, username, password):
|
||||
resp = client.post("/api/auth/login", json={
|
||||
"username": username, "password": password,
|
||||
})
|
||||
return resp.json()["access_token"]
|
||||
|
||||
def test_create_user_provisions_home(self, auth_client, monkeypatch):
|
||||
"""POST /admin/users → dossier + vault home-<user> + isolation + retrait."""
|
||||
from pathlib import Path
|
||||
|
||||
from backend import watcher_state
|
||||
|
||||
monkeypatch.setattr(watcher_state, "get_watcher", lambda: None)
|
||||
home = Path.cwd() / "Home"
|
||||
home.mkdir()
|
||||
monkeypatch.setenv("OBSIGATE_HOME_ROOT", str(home))
|
||||
|
||||
admin = {"Authorization": f"Bearer {self._login(auth_client, 'admin', 'chab30')}"}
|
||||
|
||||
resp = auth_client.post("/api/auth/admin/users", headers=admin, json={
|
||||
"username": "alice", "password": "testpass", "role": "user",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
assert (home / "alice").is_dir()
|
||||
|
||||
names = [v["name"] for v in auth_client.get("/api/vaults", headers=admin).json()]
|
||||
assert "home-alice" in names
|
||||
# L'octroi atterrit bien dans le user → visible dans son JWT/liste de vaults
|
||||
assert "home-alice" in resp.json()["vaults"]
|
||||
|
||||
# Isolation : alice ne voit QUE son propre dossier
|
||||
alice = {"Authorization": f"Bearer {self._login(auth_client, 'alice', 'testpass')}"}
|
||||
alice_names = [v["name"] for v in auth_client.get("/api/vaults", headers=alice).json()]
|
||||
assert alice_names == ["home-alice"]
|
||||
|
||||
# Suppression du compte → vault refermé, dossier conservé
|
||||
resp = auth_client.delete("/api/auth/admin/users/alice", headers=admin)
|
||||
assert resp.status_code == 200
|
||||
assert (home / "alice").is_dir()
|
||||
names = [v["name"] for v in auth_client.get("/api/vaults", headers=admin).json()]
|
||||
assert "home-alice" not in names
|
||||
|
||||
def test_create_user_without_home_root_is_unchanged(self, auth_client):
|
||||
"""Sans OBSIGATE_HOME_ROOT : aucun dossier, comportement historique."""
|
||||
from pathlib import Path
|
||||
|
||||
assert Path.cwd().exists()
|
||||
admin = {"Authorization": f"Bearer {self._login(auth_client, 'admin', 'chab30')}"}
|
||||
resp = auth_client.post("/api/auth/admin/users", headers=admin, json={
|
||||
"username": "nohome", "password": "testpass", "role": "user",
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
assert not (Path.cwd() / "Home").exists()
|
||||
|
||||
Reference in New Issue
Block a user