The merged PR left the review-queue's mutating endpoints ungated. Both now require admin, matching the Phase 3 destructive-endpoint convention: - /api/verification/<id>/delete (os.remove + drops the history row) — @admin_only, so a non-admin on a login/multi-profile instance can't delete library files. - /api/verification/<id>/approve (flips verification_status + writes the tag) — @admin_only; also wrapped its DB writes in `with db._get_connection()` for rollback-on-error + codebase consistency (was a bare conn). Read/playback endpoints (stream/play/compare/entry/config) stay open — the app's LAN-read model. Tests: non-admin gets 403 on delete + approve; admin isn't blocked.
45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
"""#845 follow-up: the mutating verification-review endpoints (delete removes a
|
|
file from disk; approve flips verification state) must be admin-only, matching the
|
|
Phase 3 destructive-endpoint gating. The read/playback ones stay open."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
import pytest
|
|
|
|
_TMP = tempfile.mkdtemp(prefix='soulsync-testdb-vgate-')
|
|
os.environ['DATABASE_PATH'] = os.path.join(_TMP, 'v.db')
|
|
os.environ['SOULSYNC_TEST_DB_READY'] = '1'
|
|
|
|
web_server = pytest.importorskip('web_server')
|
|
|
|
|
|
@pytest.fixture
|
|
def client():
|
|
return web_server.app.test_client()
|
|
|
|
|
|
def _as_nonadmin(client):
|
|
pid = web_server.get_database().create_profile(name=f'u_{os.urandom(4).hex()}')
|
|
with client.session_transaction() as s:
|
|
s['profile_id'] = pid
|
|
return pid
|
|
|
|
|
|
def test_delete_is_admin_only(client):
|
|
_as_nonadmin(client)
|
|
assert client.post('/api/verification/1/delete').status_code == 403
|
|
|
|
|
|
def test_approve_is_admin_only(client):
|
|
_as_nonadmin(client)
|
|
assert client.post('/api/verification/1/approve').status_code == 403
|
|
|
|
|
|
def test_admin_not_blocked(client):
|
|
# default session resolves to admin (profile 1) — must pass the gate (a
|
|
# missing history id yields 404, NOT 403).
|
|
assert client.post('/api/verification/999999/delete').status_code != 403
|
|
assert client.post('/api/verification/999999/approve').status_code != 403
|