feat(stock-webai): add X-WebAI-Key auth dependency + tests
verify_webai_key FastAPI dependency: 401 on missing/wrong key, 503 when WEBAI_API_KEY env unset. 4 unit tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
28
stock/app/auth.py
Normal file
28
stock/app/auth.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from fastapi import Header, HTTPException
|
||||||
|
from starlette.requests import Request
|
||||||
|
|
||||||
|
logger = logging.getLogger("stock")
|
||||||
|
|
||||||
|
|
||||||
|
def verify_webai_key(
|
||||||
|
request: Request,
|
||||||
|
x_webai_key: str | None = Header(default=None, alias="X-WebAI-Key"),
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
/api/webai/* 보호용 FastAPI dependency.
|
||||||
|
|
||||||
|
- WEBAI_API_KEY env 미설정 → 503 (다른 endpoint 무영향)
|
||||||
|
- 헤더 누락 또는 키 불일치 → 401 + logger.warning(ip)
|
||||||
|
"""
|
||||||
|
configured = os.getenv("WEBAI_API_KEY", "").strip()
|
||||||
|
if not configured:
|
||||||
|
logger.error("WEBAI_API_KEY not configured — refusing /api/webai/* request")
|
||||||
|
raise HTTPException(status_code=503, detail="webai auth not configured")
|
||||||
|
|
||||||
|
if not x_webai_key or x_webai_key != configured:
|
||||||
|
remote = request.client.host if request.client else "?"
|
||||||
|
logger.warning("auth_fail path=%s remote=%s", request.url.path, remote)
|
||||||
|
raise HTTPException(status_code=401, detail="invalid or missing X-WebAI-Key")
|
||||||
45
stock/app/test_webai_auth.py
Normal file
45
stock/app/test_webai_auth.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import pytest
|
||||||
|
from fastapi import HTTPException
|
||||||
|
from starlette.requests import Request
|
||||||
|
|
||||||
|
|
||||||
|
def _make_request() -> Request:
|
||||||
|
"""Minimal Request stub for verify_webai_key (only request.url.path + request.client used)."""
|
||||||
|
scope = {
|
||||||
|
"type": "http",
|
||||||
|
"path": "/api/webai/test",
|
||||||
|
"headers": [],
|
||||||
|
"client": ("1.2.3.4", 12345),
|
||||||
|
}
|
||||||
|
return Request(scope=scope)
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_with_valid_key_passes(monkeypatch):
|
||||||
|
monkeypatch.setenv("WEBAI_API_KEY", "secret-key-abc")
|
||||||
|
from app.auth import verify_webai_key
|
||||||
|
verify_webai_key(_make_request(), x_webai_key="secret-key-abc")
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_without_key_raises_401(monkeypatch):
|
||||||
|
monkeypatch.setenv("WEBAI_API_KEY", "secret-key-abc")
|
||||||
|
from app.auth import verify_webai_key
|
||||||
|
with pytest.raises(HTTPException) as exc:
|
||||||
|
verify_webai_key(_make_request(), x_webai_key=None)
|
||||||
|
assert exc.value.status_code == 401
|
||||||
|
assert "X-WebAI-Key" in exc.value.detail
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_with_wrong_key_raises_401(monkeypatch):
|
||||||
|
monkeypatch.setenv("WEBAI_API_KEY", "secret-key-abc")
|
||||||
|
from app.auth import verify_webai_key
|
||||||
|
with pytest.raises(HTTPException) as exc:
|
||||||
|
verify_webai_key(_make_request(), x_webai_key="wrong-key")
|
||||||
|
assert exc.value.status_code == 401
|
||||||
|
|
||||||
|
|
||||||
|
def test_verify_returns_503_when_env_missing(monkeypatch):
|
||||||
|
monkeypatch.delenv("WEBAI_API_KEY", raising=False)
|
||||||
|
from app.auth import verify_webai_key
|
||||||
|
with pytest.raises(HTTPException) as exc:
|
||||||
|
verify_webai_key(_make_request(), x_webai_key="anything")
|
||||||
|
assert exc.value.status_code == 503
|
||||||
Reference in New Issue
Block a user