mirror of
https://github.com/zvx-echo6/central.git
synced 2026-05-22 10:34:43 +02:00
fix(gui): add form-based CSRF validation and fix index context
- Add _validate_csrf_form helper for form-based CSRF token validation (compares form csrf_token with fastapi-csrf-token cookie) - Fix index route to pass operator and csrf_token to template context Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b1ba2d1863
commit
c529708c75
1 changed files with 24 additions and 6 deletions
|
|
@ -24,6 +24,19 @@ from central.gui.db import get_pool
|
|||
router = APIRouter()
|
||||
|
||||
|
||||
async def _validate_csrf_form(request, csrf_protect):
|
||||
"""Validate CSRF token from form data."""
|
||||
form = await request.form()
|
||||
csrf_token = form.get("csrf_token")
|
||||
if csrf_token:
|
||||
cookie_token = request.cookies.get("fastapi-csrf-token")
|
||||
if not cookie_token or cookie_token != csrf_token:
|
||||
from fastapi_csrf_protect.exceptions import TokenValidationError
|
||||
raise TokenValidationError("CSRF token mismatch")
|
||||
else:
|
||||
from fastapi_csrf_protect.exceptions import MissingTokenError
|
||||
raise MissingTokenError("Missing CSRF token in form")
|
||||
|
||||
def _get_templates():
|
||||
"""Get templates instance (deferred import to avoid circular)."""
|
||||
from central.gui import templates
|
||||
|
|
@ -62,13 +75,18 @@ async def health() -> dict:
|
|||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request) -> HTMLResponse:
|
||||
async def index(request: Request, csrf_protect: CsrfProtect = Depends()) -> HTMLResponse:
|
||||
"""Render the index page."""
|
||||
templates = _get_templates()
|
||||
return templates.TemplateResponse(
|
||||
operator = getattr(request.state, "operator", None)
|
||||
csrf_token, signed_token = csrf_protect.generate_csrf_tokens()
|
||||
response = templates.TemplateResponse(
|
||||
request=request,
|
||||
name="index.html",
|
||||
context={"operator": operator, "csrf_token": signed_token},
|
||||
)
|
||||
csrf_protect.set_csrf_cookie(signed_token, response)
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/setup", response_class=HTMLResponse)
|
||||
|
|
@ -101,7 +119,7 @@ async def setup_submit(
|
|||
pool = get_pool()
|
||||
|
||||
# Validate CSRF
|
||||
await csrf_protect.validate_csrf(request)
|
||||
await _validate_csrf_form(request, csrf_protect)
|
||||
|
||||
# Validate input
|
||||
error = None
|
||||
|
|
@ -195,7 +213,7 @@ async def login_submit(
|
|||
pool = get_pool()
|
||||
|
||||
# Validate CSRF
|
||||
await csrf_protect.validate_csrf(request)
|
||||
await _validate_csrf_form(request, csrf_protect)
|
||||
|
||||
# Look up operator
|
||||
async with pool.acquire() as conn:
|
||||
|
|
@ -261,7 +279,7 @@ async def logout(
|
|||
pool = get_pool()
|
||||
|
||||
# Validate CSRF
|
||||
await csrf_protect.validate_csrf(request)
|
||||
await _validate_csrf_form(request, csrf_protect)
|
||||
|
||||
# Get current session
|
||||
session_token = request.cookies.get("central_session")
|
||||
|
|
@ -310,7 +328,7 @@ async def change_password_submit(
|
|||
operator = request.state.operator
|
||||
|
||||
# Validate CSRF
|
||||
await csrf_protect.validate_csrf(request)
|
||||
await _validate_csrf_form(request, csrf_protect)
|
||||
|
||||
# Get current password hash
|
||||
async with pool.acquire() as conn:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue