a829bf3eb0
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
81 lines
3.6 KiB
Python
81 lines
3.6 KiB
Python
"""TieMeasureFlow Client Configuration."""
|
|
import os
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv(os.path.join(os.path.dirname(__file__), '..', '.env'))
|
|
|
|
# Committed default for CLIENT_SECRET_KEY (also in .env.example). A Flask
|
|
# session is only as secret as this value: left at the default, anyone can
|
|
# forge a session cookie (roles, is_admin) - see guard_default_secret_key (#33).
|
|
DEFAULT_SECRET_KEY = "change-this-to-another-random-secret-key"
|
|
|
|
|
|
class Config:
|
|
"""Flask client configuration."""
|
|
|
|
# Flask
|
|
SECRET_KEY = os.getenv("CLIENT_SECRET_KEY", DEFAULT_SECRET_KEY)
|
|
DEBUG = os.getenv("FLASK_DEBUG", "0") == "1"
|
|
|
|
# API Server connection
|
|
API_SERVER_URL = os.getenv("API_SERVER_URL", "http://localhost:8000")
|
|
|
|
# Session
|
|
SESSION_COOKIE_HTTPONLY = True
|
|
SESSION_COOKIE_SAMESITE = "Lax"
|
|
# Default: secure unless FLASK_DEBUG=1 (unchanged). Explicit override via
|
|
# SESSION_COOKIE_SECURE=false for the on-premise install (#38.4): nginx
|
|
# there listens on plain HTTP inside the LAN, and a cookie marked Secure
|
|
# is silently dropped by the browser on a non-HTTPS origin - login looks
|
|
# like it works and the session never sticks.
|
|
_session_cookie_secure_env = os.getenv("SESSION_COOKIE_SECURE")
|
|
if _session_cookie_secure_env is not None:
|
|
SESSION_COOKIE_SECURE = _session_cookie_secure_env.strip().lower() not in (
|
|
"false", "0", "no",
|
|
)
|
|
else:
|
|
SESSION_COOKIE_SECURE = not DEBUG
|
|
PERMANENT_SESSION_LIFETIME = 28800 # 8 hours
|
|
# None = token valid for the life of the session (#12): a measurement page
|
|
# stays open for hours between saves, and a 1h-expired token turned every
|
|
# late save into a silent "Errore di rete" for the operator.
|
|
WTF_CSRF_TIME_LIMIT = None
|
|
|
|
# Station identity: each deployed client container sets this to the station
|
|
# code it belongs to. Empty/None means "not configured".
|
|
STATION_CODE: str | None = os.getenv("STATION_CODE") or None
|
|
|
|
# Allow switching the current station from the URL (?station=CODE), keeping the
|
|
# override in the session. Meant for commissioning: it lets one PC exercise
|
|
# several stations instead of needing one machine per station.
|
|
# Off by default - on the shop floor the station identity comes from the local
|
|
# install, and measuring against the wrong station's recipes would silently
|
|
# break traceability.
|
|
STATION_SWITCH_ENABLED: bool = os.getenv("STATION_SWITCH_ENABLED", "0") == "1"
|
|
|
|
# Babel i18n
|
|
BABEL_DEFAULT_LOCALE = "it"
|
|
BABEL_DEFAULT_TIMEZONE = "Europe/Rome"
|
|
# Il DB e le API parlano UTC; si converte solo alla presentazione.
|
|
DISPLAY_TIMEZONE: str = os.getenv("DISPLAY_TIMEZONE", "Europe/Rome")
|
|
LANGUAGES = {"it": "Italiano", "en": "English"}
|
|
|
|
|
|
def guard_default_secret_key(app) -> None:
|
|
"""Refuse to serve when CLIENT_SECRET_KEY was left at its committed default (#33).
|
|
|
|
Checked per-request (wired as a ``before_request`` in app.py) rather than
|
|
at import/create_app time: pytest and every fixture in this suite call
|
|
``create_app()`` and only *afterwards* set ``app.config["TESTING"]``, so a
|
|
check made during app creation would always see TESTING as False and
|
|
break the whole test suite and local `flask run` alike.
|
|
"""
|
|
if app.debug or app.testing:
|
|
return
|
|
if app.config.get("SECRET_KEY") == DEFAULT_SECRET_KEY:
|
|
raise RuntimeError(
|
|
"CLIENT_SECRET_KEY non impostato: la sessione Flask (ruoli, is_admin) "
|
|
"sarebbe falsificabile da chiunque conosca il default committato. "
|
|
"Imposta CLIENT_SECRET_KEY in .env prima di avviare in produzione."
|
|
)
|