Production correct-credential login returned auth_unavailable 503 because Flask's SECRET_KEY was unset: wrong-password probes stopped at 401 before CSRF token issuance, while valid credentials reached _csrf_serializer() and crashed. App factory now rejects absent/short (<32 char) SECRET_KEY at startup, and docker_entrypoint.sh fails fast before migration/services. Bootstrap no longer passes ADMIN_PASSWORD in process arguments; env-only. Tests: app-factory + entrypoint regression (5 focused passed), full backend suite 204 passed. Independent review PASS.
60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
import os
|
|
|
|
import pytest
|
|
|
|
from app import create_app
|
|
from app.config import Config
|
|
from app.models.task import TaskManager
|
|
|
|
|
|
class TestConfig(Config):
|
|
SECRET_KEY = "test-secret-key-test-secret-key-0123456789-abcdef"
|
|
MEMORY_BACKEND = "local"
|
|
CORS_ALLOWED_ORIGINS = ["https://allowed.example"]
|
|
TESTING = True
|
|
|
|
|
|
def test_cors_uses_app_allowlist_and_credentials():
|
|
previous = os.environ.get("DATABASE_URL")
|
|
os.environ["DATABASE_URL"] = "sqlite+pysqlite:///:memory:"
|
|
try:
|
|
app = create_app(TestConfig)
|
|
response = app.test_client().options(
|
|
"/api/graph/project/list",
|
|
headers={
|
|
"Origin": "https://allowed.example",
|
|
"Access-Control-Request-Method": "GET",
|
|
},
|
|
)
|
|
assert response.headers["Access-Control-Allow-Origin"] == "https://allowed.example"
|
|
assert response.headers["Access-Control-Allow-Credentials"] == "true"
|
|
finally:
|
|
if previous is None:
|
|
os.environ.pop("DATABASE_URL", None)
|
|
else:
|
|
os.environ["DATABASE_URL"] = previous
|
|
|
|
|
|
def test_cors_wildcard_is_rejected_with_cookie_auth():
|
|
class WildcardConfig(TestConfig):
|
|
CORS_ALLOWED_ORIGINS = ["*"]
|
|
|
|
with pytest.raises(RuntimeError, match="wildcard_cors_not_allowed"):
|
|
create_app(WildcardConfig)
|
|
|
|
|
|
def test_missing_secret_key_is_rejected_at_startup():
|
|
class MissingSecretConfig(TestConfig):
|
|
SECRET_KEY = None
|
|
|
|
with pytest.raises(RuntimeError, match="secret_key_required"):
|
|
create_app(MissingSecretConfig)
|
|
|
|
|
|
def test_short_secret_key_is_rejected_at_startup():
|
|
class ShortSecretConfig(TestConfig):
|
|
SECRET_KEY = "short"
|
|
|
|
with pytest.raises(RuntimeError, match="secret_key_required"):
|
|
create_app(ShortSecretConfig)
|