fix: un dossier perso n'est plus visible par les autres comptes #194
L'admin (vaults: ["*"]) voyait le home de chaque utilisateur dans sa barre latérale : "*" ouvrait tous les vaults, home-* compris. - backend/auth/middleware.py : check_vault_access exige un octroi explicite pour tout vault home-* (nouveau is_home_vault()). - Filtres « * » en dur remplacés par check_vault_access : dashboard, conflits, liens retour, favoris, abonnements push. - /api/search : search_vaults(is_allowed=…) filtre les bruts avant pagination (total et page restent justes). - backend/user_home.py : _grant n'écarte plus les comptes « * » — l'admin reçoit son propre home-admin (auto-réparé au démarrage). - Tests : test_user_home.py +2, assertion API inversée dans test_auth_api.py (admin ne voit plus home-alice).
This commit is contained in:
@@ -24,7 +24,7 @@ from pathlib import Path
|
||||
from fastapi import APIRouter, Body, Depends, HTTPException, Query
|
||||
|
||||
from backend.ai import PROVIDERS, _read_ai_keys, get_ai_key
|
||||
from backend.auth.middleware import require_admin, require_auth
|
||||
from backend.auth.middleware import check_vault_access, require_admin, require_auth
|
||||
from backend.indexer import index
|
||||
from backend.media_types import IMAGE_EXTENSIONS
|
||||
from backend.schemas import (
|
||||
@@ -495,14 +495,13 @@ async def api_diagnostics(current_user=Depends(require_admin)):
|
||||
@router.get("/api/dashboard", response_model=DashboardResponse)
|
||||
async def api_dashboard(current_user=Depends(require_auth)):
|
||||
"""Aggregated dashboard statistics across all accessible vaults."""
|
||||
user_vaults = current_user.get("_token_vaults") or current_user.get("vaults", [])
|
||||
vault_stats = []
|
||||
total_files = 0
|
||||
total_tags = set()
|
||||
total_size = 0
|
||||
total_images = 0
|
||||
for vname, vdata in index.items():
|
||||
if "*" not in user_vaults and vname not in user_vaults:
|
||||
if not check_vault_access(vname, current_user):
|
||||
continue
|
||||
files = vdata.get("files", [])
|
||||
fc = len(files)
|
||||
|
||||
@@ -31,10 +31,9 @@ router = APIRouter(tags=["conflicts"])
|
||||
@router.get("/api/conflicts", response_model=ConflictsResponse)
|
||||
async def api_conflicts(current_user=Depends(require_auth)):
|
||||
"""List sync-conflict files across accessible vaults."""
|
||||
user_vaults = current_user.get("_token_vaults") or current_user.get("vaults", [])
|
||||
all_conflicts = get_conflicts()
|
||||
if "*" not in user_vaults:
|
||||
all_conflicts = [c for c in all_conflicts if c["vault"] in user_vaults]
|
||||
# #194 : filtrage via check_vault_access ("*" n'inclut pas les homes).
|
||||
all_conflicts = [c for c in all_conflicts if check_vault_access(c["vault"], current_user)]
|
||||
return {"conflicts": all_conflicts, "total": len(all_conflicts)}
|
||||
|
||||
|
||||
|
||||
@@ -166,12 +166,11 @@ async def api_file_backlinks(
|
||||
if not vault_data:
|
||||
raise HTTPException(status_code=404, detail=f"Vault '{vault_name}' not found")
|
||||
|
||||
user_vaults = current_user.get("_token_vaults") or current_user.get("vaults", [])
|
||||
backlinks = get_backlinks(vault_name, path)
|
||||
|
||||
# Filter by user-accessible vaults
|
||||
if "*" not in user_vaults:
|
||||
backlinks = [b for b in backlinks if b["vault"] in user_vaults]
|
||||
# Filter by user-accessible vaults (#194 : check_vault_access, "*" sans
|
||||
# les dossiers persos).
|
||||
backlinks = [b for b in backlinks if check_vault_access(b["vault"], current_user)]
|
||||
|
||||
return {
|
||||
"vault": vault_name,
|
||||
|
||||
@@ -59,7 +59,6 @@ async def api_recent(limit: int | None = Query(None), vault: str | None = Query(
|
||||
@router.get("/api/bookmarks", response_model=BookmarksResponse)
|
||||
async def api_bookmarks(vault: str | None = Query(None), current_user=Depends(require_auth)):
|
||||
username = current_user.get("username")
|
||||
user_vaults = current_user.get("_token_vaults") or current_user.get("vaults", [])
|
||||
|
||||
if not username:
|
||||
return {"files": []}
|
||||
@@ -68,7 +67,8 @@ async def api_bookmarks(vault: str | None = Query(None), current_user=Depends(re
|
||||
files_resp = []
|
||||
for item in history:
|
||||
v_name = item["vault"]
|
||||
if "*" not in user_vaults and v_name not in user_vaults:
|
||||
# #194 : check_vault_access — "*" n'inclut pas les dossiers persos.
|
||||
if not check_vault_access(v_name, current_user):
|
||||
continue
|
||||
|
||||
# Find in index to get metadata
|
||||
|
||||
@@ -82,7 +82,12 @@ async def api_search(
|
||||
# paginate in the shared service so routes and tools share the same logic.
|
||||
return await loop.run_in_executor(
|
||||
get_search_executor(),
|
||||
partial(search_vaults, q, vault, tag, limit, offset),
|
||||
# #194 : filtre par vault accessible AVANT pagination ("*" sans les
|
||||
# dossiers persos) — sinon un user voyait les notes des autres.
|
||||
partial(
|
||||
search_vaults, q, vault, tag, limit, offset,
|
||||
is_allowed=lambda v: check_vault_access(v, current_user),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user