"""HTTP caching utilities.""" import functools from flask import after_this_request, current_app def cache_max_age(ttl): """Set Cache-Control: public, max-age on 2xx responses. ttl: int (seconds) or callable returning int — callable is invoked at request time so it can read current_app.config. Pass 0 (or a callable returning 0) to disable. The ``public`` directive is required, not cosmetic: our consumers reach these endpoints with an Authorization header, and a shared cache (e.g. graphql-abacus' RESTDataSource HTTPCache, which runs http-cache-semantics in shared mode) MUST NOT store an authenticated response unless it carries ``public``, ``s-maxage``, or ``must-revalidate`` (RFC 7234 §3.2). Without ``public`` a bare ``max-age`` is silently non-storable downstream. A cached response also carries ``Vary: Authorization``. These endpoints gate access per caller (``verify_rules_access_standalone`` / ``pdp_authorize_resource``), so the same URL can legitimately return 200 for one identity and 403 for another. ``public`` alone lets a shared cache, keyed by URL, replay a stored 200 to a caller who should have been denied. ``Vary: Authorization`` keys the shared cache by credential, closing that cross-identity replay. The bytes are identical for every authorized caller, so this is defense-in-depth, not a confidentiality fix; the cost is negligible because the ~300s TTL expires long before an M2M token rotates (20-60 min), so per-token entries still serve many hits within their lifetime. """ def decorator(f): @functools.wraps(f) def wrapper(*args, **kwargs): @after_this_request def set_cache_control(response): seconds = ttl() if callable(ttl) else ttl if seconds > 0 and 200 <= response.status_code < 300: response.cache_control.public = True response.cache_control.max_age = seconds # Add, don't overwrite: flask-compress also appends # ``Accept-Encoding`` to Vary. response.vary.add('Authorization') return response return f(*args, **kwargs) return wrapper return decorator def _reference_ttl(): return current_app.config.get('OWS_REFERENCE_CACHE_TTL_SECONDS', 300) cache_reference_data = cache_max_age(_reference_ttl)