Tighten location request validation
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
import logging
|
||||
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from fastapi.responses import PlainTextResponse, Response
|
||||
@@ -10,6 +11,8 @@ from app.schemas.location import LocationRecordRequest
|
||||
from app.services.location import record_location
|
||||
|
||||
router = APIRouter(tags=["location"])
|
||||
logger = logging.getLogger(__name__)
|
||||
BAD_REQUEST_MESSAGE = "bad request"
|
||||
|
||||
|
||||
@router.post("/location/record")
|
||||
@@ -18,11 +21,15 @@ async def create_location_record(request: Request, db: Session = Depends(get_db)
|
||||
raw_payload = await request.body()
|
||||
data = json.loads(raw_payload)
|
||||
payload = LocationRecordRequest.model_validate(data)
|
||||
record_location(db, payload)
|
||||
except json.JSONDecodeError as exc:
|
||||
return PlainTextResponse(str(exc), status_code=400)
|
||||
logger.warning("Rejected location request due to invalid JSON: %s", exc)
|
||||
return PlainTextResponse(BAD_REQUEST_MESSAGE, status_code=400)
|
||||
except ValidationError as exc:
|
||||
return PlainTextResponse(str(exc), status_code=400)
|
||||
logger.warning("Rejected location request due to payload validation failure: %s", exc)
|
||||
return PlainTextResponse(BAD_REQUEST_MESSAGE, status_code=400)
|
||||
except ValueError as exc:
|
||||
logger.warning("Rejected location request due to invalid numeric input: %s", exc)
|
||||
return PlainTextResponse(BAD_REQUEST_MESSAGE, status_code=400)
|
||||
|
||||
record_location(db, payload)
|
||||
return Response(status_code=200)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ class LocationRecordRequest(BaseModel):
|
||||
person: str
|
||||
latitude: str
|
||||
longitude: str
|
||||
altitude: str = ""
|
||||
altitude: str | None = None
|
||||
|
||||
model_config = ConfigDict(extra="forbid")
|
||||
|
||||
|
||||
@@ -7,13 +7,20 @@ from app.models.location import Location
|
||||
from app.schemas.location import LocationRecordRequest
|
||||
|
||||
|
||||
def _parse_float_compat(value: str) -> float:
|
||||
def _parse_optional_float_compat(value: str | None) -> float:
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError):
|
||||
return 0.0
|
||||
|
||||
|
||||
def _parse_required_float(value: str, field_name: str) -> float:
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise ValueError(f"Invalid numeric value for {field_name}") from exc
|
||||
|
||||
|
||||
def _utc_now_rfc3339() -> str:
|
||||
now = datetime.now(timezone.utc).replace(microsecond=0)
|
||||
return now.isoformat().replace("+00:00", "Z")
|
||||
@@ -26,11 +33,10 @@ def record_location(session: Session, payload: LocationRecordRequest) -> None:
|
||||
.values(
|
||||
person=payload.person,
|
||||
datetime=_utc_now_rfc3339(),
|
||||
latitude=_parse_float_compat(payload.latitude),
|
||||
longitude=_parse_float_compat(payload.longitude),
|
||||
altitude=_parse_float_compat(payload.altitude),
|
||||
latitude=_parse_required_float(payload.latitude, "latitude"),
|
||||
longitude=_parse_required_float(payload.longitude, "longitude"),
|
||||
altitude=_parse_optional_float_compat(payload.altitude),
|
||||
)
|
||||
)
|
||||
session.execute(stmt)
|
||||
session.commit()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user