|
6 | 6 |
|
7 | 7 | from config.config_manager import MetadataMediaType |
8 | 8 | from handler.database import db_collection_handler, db_rom_handler |
| 9 | +from handler.database.base_handler import sync_session |
9 | 10 | from handler.filesystem.resources_handler import FSResourcesHandler |
10 | 11 | from handler.filesystem.roms_handler import FSRomsHandler |
11 | 12 | from handler.metadata.flashpoint_handler import FlashpointHandler, FlashpointRom |
|
16 | 17 | from handler.metadata.ra_handler import RAGameRom, RAHandler |
17 | 18 | from handler.metadata.ss_handler import SSHandler, SSRom |
18 | 19 | from models.collection import Collection |
| 20 | +from models.permission import HiddenEntity, PermEntity |
19 | 21 | from models.platform import Platform |
20 | 22 | from models.rom import Rom, RomFile, compute_name_sort_key |
21 | 23 | from models.user import User |
@@ -318,6 +320,50 @@ def test_get_rom_content_missing_rom_returns_404(client: TestClient, access_toke |
318 | 320 | assert response.status_code == status.HTTP_404_NOT_FOUND |
319 | 321 |
|
320 | 322 |
|
| 323 | +def _hide_rom_for_user(rom_id: int, user_id: int) -> None: |
| 324 | + with sync_session.begin() as s: |
| 325 | + s.add(HiddenEntity(entity=PermEntity.ROMS, entity_id=rom_id, user_id=user_id)) |
| 326 | + |
| 327 | + |
| 328 | +def test_get_romfile_content_visible_rom( |
| 329 | + client: TestClient, viewer_access_token: str, rom: Rom, rom_file |
| 330 | +): |
| 331 | + # Baseline: a rom the viewer can see is downloadable by direct RomFile.id. |
| 332 | + response = client.get( |
| 333 | + f"/api/roms/{rom_file.id}/files/content/whatever.bin", |
| 334 | + headers={"Authorization": f"Bearer {viewer_access_token}"}, |
| 335 | + follow_redirects=False, |
| 336 | + ) |
| 337 | + assert response.status_code == status.HTTP_200_OK |
| 338 | + assert "X-Accel-Redirect" in response.headers |
| 339 | + |
| 340 | + |
| 341 | +def test_get_romfile_content_hidden_rom_returns_404( |
| 342 | + client: TestClient, viewer_access_token: str, viewer_user, rom: Rom, rom_file |
| 343 | +): |
| 344 | + # A file belonging to a hidden rom must 404 even by direct RomFile.id, |
| 345 | + # matching the ROM-level content endpoint (visibility-bypass regression). |
| 346 | + _hide_rom_for_user(rom.id, viewer_user.id) |
| 347 | + response = client.get( |
| 348 | + f"/api/roms/{rom_file.id}/files/content/whatever.bin", |
| 349 | + headers={"Authorization": f"Bearer {viewer_access_token}"}, |
| 350 | + follow_redirects=False, |
| 351 | + ) |
| 352 | + assert response.status_code == status.HTTP_404_NOT_FOUND |
| 353 | + |
| 354 | + |
| 355 | +def test_get_romfile_hidden_rom_returns_404( |
| 356 | + client: TestClient, viewer_access_token: str, viewer_user, rom: Rom, rom_file |
| 357 | +): |
| 358 | + # The file metadata endpoint must not leak files of a hidden rom either. |
| 359 | + _hide_rom_for_user(rom.id, viewer_user.id) |
| 360 | + response = client.get( |
| 361 | + f"/api/roms/{rom_file.id}/files", |
| 362 | + headers={"Authorization": f"Bearer {viewer_access_token}"}, |
| 363 | + ) |
| 364 | + assert response.status_code == status.HTTP_404_NOT_FOUND |
| 365 | + |
| 366 | + |
321 | 367 | @patch.object(FSRomsHandler, "rename_fs_rom") |
322 | 368 | @patch.object(IGDBHandler, "get_rom_by_id", return_value=IGDBRom(igdb_id=None)) |
323 | 369 | def test_update_rom( |
|
0 commit comments