"""TieMeasureFlow Client Configuration.""" import os import tomllib from pathlib import Path 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" def _read_app_version() -> str: """Versione del prodotto = `[project] version` di pyproject.toml (#70). Si risale dalla cartella del modulo: nel container il file sta in /app/pyproject.toml (Dockerfile.frontend), nel repo alla radice. Se non si trova o e' illeggibile torna "dev": il piede di pagina non deve mai rompere l'avvio. """ for parent in Path(__file__).resolve().parents: candidate = parent / "pyproject.toml" if candidate.is_file(): try: with candidate.open("rb") as fh: return str(tomllib.load(fh)["project"]["version"]) except (OSError, tomllib.TOMLDecodeError, KeyError): return "dev" return "dev" class Config: """Flask client configuration.""" # Flask SECRET_KEY = os.getenv("CLIENT_SECRET_KEY", DEFAULT_SECRET_KEY) DEBUG = os.getenv("FLASK_DEBUG", "0") == "1" # Versione e commit mostrati nel piede di pagina (#70). Letti una volta # all'import. Il commit arriva dal build arg APP_COMMIT (.git non e' nel # contesto di build, vedi .dockerignore); facoltativo: vuoto = non mostrato. APP_VERSION: str = _read_app_version() APP_COMMIT: str = os.environ.get("APP_COMMIT", "")[:7] # 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." )