fix: partage dirigé — ACL court-circuitée après résolution Partage/ + chemin canonique au token #196
CI / lint (push) Successful in 3m0s
CI / security (push) Failing after 1m38s
CI / test (push) Successful in 4m58s
CI / build (push) Successful in 1m36s
CI / e2e (push) Successful in 19m55s

- le partage EST l'autorisation: plus de 403 'Accès refusé à la vault' pour un
  destinataire sans accès au vault émetteur (api_file, raw, download)
- chemin virtuel canonique Partage/<token>/<nom> — désambiguïse les homonymes,
  fallback par nom conservé
- 'reçu' = partagé_avec contient l'utilisateur: un admin ne voit plus les
  partages des autres comme reçus (fix File not found: Partage/…)
- +3 tests (14 total)
This commit is contained in:
2026-10-10 23:11:12 -04:00
parent de6bde1613
commit 2be0b15bcf
14 changed files with 139 additions and 35 deletions
+44 -4
View File
@@ -169,24 +169,40 @@ class TestDirectedShareGate:
assert not [r for r in resp.json()["results"] if r.get("share_token")]
def test_recipient_opens_shared_file_in_app(self, sessions):
# #196 : /api/file résout home-<user>/Partage/<fichier> vers la source.
# #196 : /api/file résout home-<user>/Partage/<token>/<f> vers la
# source — même quand le destinataire N'A PAS accès au vault source
# (le partage dirigé EST l'autorisation).
admin_client, user_client = sessions
path = _ensure_shared_file()
resp = admin_client.post("/api/share/TestVault", json={
"path": path, "shared_with": ["normaluser"],
})
assert resp.status_code == 200
r = user_client.get("/api/file/home-normaluser", params={"path": "Partage/share_directed.md"})
token = resp.json()["token"]
# Retire l'accès direct au vault source : le partage doit suffire.
from backend.auth.user_store import update_user
update_user("normaluser", {"vaults": ["home-normaluser"]})
# Chemin canonique avec token
r = user_client.get("/api/file/home-normaluser", params={"path": f"Partage/{token}/share_directed.md"})
assert r.status_code == 200, r.text
body = r.json()
assert body["is_markdown"] is True
assert "dirigé" in body["html"]
# raw endpoint too
r = user_client.get("/api/file/home-normaluser/raw", params={"path": "Partage/share_directed.md"})
# Fallback par nom (liens sans token)
r = user_client.get("/api/file/home-normaluser", params={"path": "Partage/share_directed.md"})
assert r.status_code == 200, r.text
# raw + download too
r = user_client.get("/api/file/home-normaluser/raw", params={"path": f"Partage/{token}/share_directed.md"})
assert r.status_code == 200
assert "dirigé" in r.json()["raw"]
# Restore fixture state for other tests
update_user("normaluser", {"vaults": ["TestVault"]})
def test_shared_file_resolution_is_user_scoped(self, sessions):
# home-admin/Partage/x.md demandé par normaluser → pas de mapping
# (le home d'un autre user lui est interdit, 403 avant tout).
@@ -197,3 +213,27 @@ class TestDirectedShareGate:
})
r = user_client.get("/api/file/home-admin", params={"path": "Partage/share_directed.md"})
assert r.status_code == 403
def test_same_name_two_shares_token_disambiguates(self, sessions):
# Deux partages de fichiers homonymes reçus : le token sélectionne
# le bon, le fallback par nom prend le premier sans crash.
admin_client, user_client = sessions
import os as _os
vault = Path(_os.environ["VAULT_1_PATH"])
(vault / "a.md").write_text("contenu alpha unique", encoding="utf-8")
(vault / "b.md").write_text("contenu beta unique", encoding="utf-8")
# subdirectory with same basename to force ambiguity
(vault / "d1").mkdir(exist_ok=True)
(vault / "d2").mkdir(exist_ok=True)
(vault / "d1" / "same.md").write_text("contenu GAMMA unique", encoding="utf-8")
(vault / "d2" / "same.md").write_text("contenu DELTA unique", encoding="utf-8")
r1 = admin_client.post("/api/share/TestVault", json={"path": "d1/same.md", "shared_with": ["normaluser"]})
r2 = admin_client.post("/api/share/TestVault", json={"path": "d2/same.md", "shared_with": ["normaluser"]})
t1, t2 = r1.json()["token"], r2.json()["token"]
r = user_client.get("/api/file/home-normaluser", params={"path": f"Partage/{t2}/same.md"})
assert r.status_code == 200
assert "DELTA" in r.json()["html"]
r = user_client.get("/api/file/home-normaluser", params={"path": f"Partage/{t1}/same.md"})
assert r.status_code == 200
assert "GAMMA" in r.json()["html"]