mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-19 04:40:32 -05:00
786d88bdba
Pure formatting pass to satisfy ``./scripts/run-ci-local.sh code-quality``: no behavioural changes, just consistent line wrapping, import ordering, and trailing-newline normalization across routes, models, services, and utility modules.
80 lines
2.1 KiB
Python
80 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import base64
|
|
import os
|
|
from typing import Optional
|
|
|
|
_FERNET = None
|
|
|
|
|
|
def _load_key_from_env() -> str:
|
|
"""
|
|
Return a base64 urlsafe-encoded 32-byte key suitable for cryptography.fernet.Fernet.
|
|
|
|
Supported env vars:
|
|
- SETTINGS_ENCRYPTION_KEY: full key string
|
|
- SETTINGS_ENCRYPTION_KEY_FILE: file containing the key on first line
|
|
"""
|
|
key = (os.getenv("SETTINGS_ENCRYPTION_KEY") or "").strip()
|
|
if not key:
|
|
key_file = (os.getenv("SETTINGS_ENCRYPTION_KEY_FILE") or "").strip()
|
|
if key_file:
|
|
try:
|
|
with open(key_file, "r", encoding="utf-8") as f:
|
|
key = (f.read().strip().split("\n")[0] or "").strip()
|
|
except OSError:
|
|
key = ""
|
|
return key
|
|
|
|
|
|
def is_configured() -> bool:
|
|
return bool(_load_key_from_env())
|
|
|
|
|
|
def get_fernet():
|
|
global _FERNET
|
|
if _FERNET is not None:
|
|
return _FERNET
|
|
|
|
from cryptography.fernet import Fernet
|
|
|
|
key = _load_key_from_env()
|
|
if not key:
|
|
raise RuntimeError("SETTINGS_ENCRYPTION_KEY is not configured")
|
|
|
|
# Validate it looks like a Fernet key.
|
|
try:
|
|
raw = base64.urlsafe_b64decode(key.encode("utf-8"))
|
|
if len(raw) != 32:
|
|
raise ValueError("wrong key length")
|
|
except Exception as e:
|
|
raise RuntimeError("SETTINGS_ENCRYPTION_KEY is invalid (must be Fernet key)") from e
|
|
|
|
_FERNET = Fernet(key.encode("utf-8"))
|
|
return _FERNET
|
|
|
|
|
|
ENC_PREFIX = "enc:v1:"
|
|
|
|
|
|
def encrypt_if_possible(value: str) -> str:
|
|
value = (value or "").strip()
|
|
if not value:
|
|
return ""
|
|
if value.startswith(ENC_PREFIX):
|
|
return value
|
|
f = get_fernet()
|
|
token = f.encrypt(value.encode("utf-8")).decode("utf-8")
|
|
return f"{ENC_PREFIX}{token}"
|
|
|
|
|
|
def decrypt_if_needed(value: Optional[str]) -> str:
|
|
value = (value or "").strip()
|
|
if not value:
|
|
return ""
|
|
if not value.startswith(ENC_PREFIX):
|
|
return value
|
|
token = value[len(ENC_PREFIX) :]
|
|
f = get_fernet()
|
|
return f.decrypt(token.encode("utf-8")).decode("utf-8")
|