Fix CSRF 403, improve registry UI, add phonetic IDs
- Fix CSRF token handling for production: generate tokens with HMAC, set cookie via afterware, inject into HTMX requests via JS - Improve registry page: filter at top with better proportions, compact horizontal pill layout for facets - Add phonetic ID encoding (e.g., "tobi-kafu-meli") for animal display instead of truncated ULIDs - Remove "subadult" life stage (migration converts to juvenile) - Change "Death (natural)" outcome label to just "Death" - Show sex/life stage in animal picker alongside species/location 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,39 @@
|
||||
# ABOUTME: Tests for CSRF validation logic.
|
||||
# ABOUTME: Covers token matching, origin/referer validation, and safe methods.
|
||||
# ABOUTME: Covers token generation, token matching, origin/referer validation, and safe methods.
|
||||
|
||||
|
||||
class TestGenerateCSRFToken:
|
||||
"""Tests for CSRF token generation."""
|
||||
|
||||
def test_generates_token_with_nonce_and_signature(self):
|
||||
"""Token format is nonce:signature."""
|
||||
from animaltrack.web.middleware import generate_csrf_token
|
||||
|
||||
token = generate_csrf_token("test-secret")
|
||||
parts = token.split(":")
|
||||
assert len(parts) == 2
|
||||
# Nonce is 32 hex chars (16 bytes)
|
||||
assert len(parts[0]) == 32
|
||||
# Signature is 64 hex chars (SHA256)
|
||||
assert len(parts[1]) == 64
|
||||
|
||||
def test_generates_unique_tokens(self):
|
||||
"""Each call generates a different token (random nonce)."""
|
||||
from animaltrack.web.middleware import generate_csrf_token
|
||||
|
||||
token1 = generate_csrf_token("test-secret")
|
||||
token2 = generate_csrf_token("test-secret")
|
||||
assert token1 != token2
|
||||
|
||||
def test_tokens_are_hex_encoded(self):
|
||||
"""Token parts are valid hex strings."""
|
||||
from animaltrack.web.middleware import generate_csrf_token
|
||||
|
||||
token = generate_csrf_token("test-secret")
|
||||
nonce, signature = token.split(":")
|
||||
# These should not raise ValueError
|
||||
int(nonce, 16)
|
||||
int(signature, 16)
|
||||
|
||||
|
||||
class TestValidateCSRFToken:
|
||||
|
||||
Reference in New Issue
Block a user