"""四位 Key 录入面板的图片链路:这是手机端最主要的入口,必须整条走通。""" import hashlib import hmac import pytest from conftest import png_bytes, record_payload @pytest.fixture() def entry(client, admin): """用管理员账号配好四位 Key,再换成录入会话。""" from app.config import settings from app.main import ENTRY_COOKIE admin_client, admin_headers = admin response = admin_client.put("/api/me/entry-key", headers=admin_headers, json={"key": "1379", "password": settings.admin_password}) assert response.status_code == 200, response.text client.cookies.clear() assert client.post("/api/entry/auth", json={"key": "1379"}).status_code == 200 token = client.cookies.get(ENTRY_COOKIE) csrf = hmac.new(settings.secret.encode(), f"entry:{token}".encode(), hashlib.sha256).hexdigest() return client, {"X-CSRF-Token": csrf} def entry_upload(client, headers, **kwargs): files = {"file": ("field.png", png_bytes(48, 36), "image/png"), "thumbnail": ("t.png", png_bytes(12, 9), "image/png")} return client.post("/api/entry/uploads/images", headers=headers, files=files, **kwargs) def test_entry_upload_requires_the_entry_session(client): client.cookies.clear() assert client.post("/api/entry/uploads/images", files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 401 def test_entry_upload_requires_the_entry_csrf_token(entry): client, _ = entry assert client.post("/api/entry/uploads/images", files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 403 def test_admin_csrf_token_is_not_accepted_on_the_entry_endpoint(entry, admin): """两套会话的 CSRF 派生方式不同,管理端令牌不能拿来打录入接口。""" client, _ = entry _, admin_headers = admin assert client.post("/api/entry/uploads/images", headers=admin_headers, files={"file": ("a.png", png_bytes(), "image/png")}).status_code == 403 def test_entry_record_is_saved_with_its_images(entry): from app.config import settings client, headers = entry token = entry_upload(client, headers).json()["data"]["token"] payload = record_payload("LAS-ENTRY-001", image_tokens=[token], confirm_username=settings.admin_username) created = client.post("/api/entry/records", headers=headers, json=payload) assert created.status_code == 200, created.text images = client.get(f"/api/public/records/{created.json()['data']['id']}").json()["data"]["images"] assert len(images) == 1 and images[0]["width"] == 48 def test_entry_record_rejects_a_mismatched_username(entry): client, headers = entry token = entry_upload(client, headers).json()["data"]["token"] payload = record_payload("LAS-ENTRY-002", image_tokens=[token], confirm_username="someone-else") assert client.post("/api/entry/records", headers=headers, json=payload).status_code == 403 def test_entry_pending_image_can_be_discarded(entry): client, headers = entry token = entry_upload(client, headers).json()["data"]["token"] assert client.delete(f"/api/entry/uploads/images/{token}", headers=headers).status_code == 200 assert client.delete(f"/api/entry/uploads/images/{token}", headers=headers).status_code == 404 def test_pending_image_of_another_account_cannot_be_claimed(client, admin): """令牌必须同时校验归属,否则拿到别人的令牌就能把图挂到自己的记录上。""" from app.config import settings from app.main import SESSION_COOKIE admin_client, admin_headers = admin created = admin_client.post("/api/users", headers=admin_headers, json={"username": "uploader9", "display_name": "录入九", "password": "UploaderPass123", "role": "uploader"}) assert created.status_code in (200, 409) # 换成另一个账号上传,拿到属于它的令牌 client.cookies.clear() client.post("/api/auth/login", data={"username": "uploader9", "password": "UploaderPass123"}) other_csrf = hmac.new(settings.secret.encode(), client.cookies.get(SESSION_COOKIE).encode(), hashlib.sha256).hexdigest() stolen = client.post("/api/uploads/images", headers={"X-CSRF-Token": other_csrf}, files={"file": ("mine.png", png_bytes(20, 20), "image/png")}).json()["data"]["token"] # 换回管理员,用别人的令牌建记录必须失败 client.cookies.clear() client.post("/api/auth/login", data={"username": settings.admin_username, "password": settings.admin_password}) admin_csrf = hmac.new(settings.secret.encode(), client.cookies.get(SESSION_COOKIE).encode(), hashlib.sha256).hexdigest() response = client.post("/api/records", headers={"X-CSRF-Token": admin_csrf}, json=record_payload("LAS-STEAL-001", image_tokens=[stolen])) assert response.status_code == 404 def test_thumbnail_variant_reports_its_own_media_type(entry): from app.config import settings client, headers = entry body = client.post("/api/entry/uploads/images", headers=headers, files={ "file": ("photo.jpg", open("/System/Library/CoreServices/DefaultBackground.jpg", "rb").read() if False else png_bytes(64, 64), "image/png"), "thumbnail": ("t.png", png_bytes(16, 16), "image/png"), }).json() token = body["data"]["token"] payload = record_payload("LAS-ENTRY-004", image_tokens=[token], confirm_username=settings.admin_username) record_id = client.post("/api/entry/records", headers=headers, json=payload).json()["data"]["id"] image = client.get(f"/api/public/records/{record_id}").json()["data"]["images"][0] assert "variant=thumb" in image["thumb_url"] response = client.get(image["thumb_url"]) assert response.status_code == 200 and response.headers["content-type"] == "image/png" assert response.headers["etag"].endswith('-thumb"')