This update modifies permission classes in multiple viewsets to restrict access based on user roles. The `IsAdmin` permission is now enforced for several actions, including group management and permission listing, ensuring that only administrators can perform sensitive operations. Additionally, a new utility function, `resolve_safe_local_data_path`, is introduced to enhance security when accessing local file paths. The changes improve overall security and maintainability of the codebase.
This update introduces a new `is_catchup_enabled` function to determine if catch-up is allowed for users based on their custom properties and system settings. The `UserViewSet` is modified to restrict admin-managed properties, including catch-up access. Additionally, various views and tests are updated to incorporate catch-up checks, ensuring that users without access receive appropriate error responses. The frontend is enhanced with a catch-up toggle in user and system settings forms, allowing for better management of catch-up capabilities.
This update introduces Redis caching for grouped settings in the CoreSettings model, improving performance by reducing database queries. A new method, `get_network_access_settings`, is added to streamline access to network settings. Additionally, the `setting_flag_enabled` function is introduced to handle boolean settings more robustly. The cache is invalidated on save and delete operations, ensuring data consistency. Tests have been added to validate the caching behavior and the new settings functionality.
DB sessions were never actually pinned to UTC. Three stacked failures:
1. The connection_created receiver (_force_utc0 in core/apps.py) was a
nested closure connected with Django's default weak reference. It was
garbage-collected as soon as CoreConfig.ready() returned, so
SET TIME ZONE 'UTC0' fired into a dead weakref and never executed -
in any version since it landed (verified: the signal's receivers list
on a live 0.27.2 shows a dead weakref).
2. Sessions were UTC on older stacks anyway because Django's own
init_connection_state configured the timezone. Since native
psycopg-pool support (Django 5.1+), that path is gated on
`not self.pool` - and the geventpool mixin's `pool` property is
always truthy, so Django silently skips timezone (and role)
configuration for this backend on every connection. This is what
actually regressed at the psycopg2->3 / Django upgrade: the masking
layer disappeared, not the (already dead) signal.
3. psycopg3 logs "unknown PostgreSQL timezone: 'UTC0'" because the
POSIX spec is unresolvable in Python zoneinfo (cosmetic, but it
means 'UTC0' buys nothing on psycopg3).
Net effect: every session ran at the server-default timezone (verified
live: SHOW TimeZone through the pool returns 'Etc/UTC'). Deployments
whose Postgres default is non-UTC (e.g. /etc/localtime bind-mounts,
the original issue 651 report) get the EPG offset corruption back.
Fix: pin the GUC in the libpq startup packet in the pool backend's
get_connection_params():
- covers every connection the gevent pool creates; no signal, GC, or
Django-flow dependency (same pattern as the client_encoding pin the
pool already applies)
- startup-packet GUCs are the session default: they survive ROLLBACK,
and RESET TimeZone returns to UTC rather than the server default
- 'UTC' resolves cleanly in psycopg3's zoneinfo lookup, and PostgreSQL
resolves it against its own bundled tzdata, not host-mounted files
The dead signal is removed. Regression tests exercise the session
timezone through the real pool backend explicitly (the test runner's
default engine is the vanilla Django backend), and assert the
RESET-returns-to-UTC session-default property.
Tested: live 0.27.2 AIO (psycopg 3.3.4, PostgreSQL 17) - unpatched
backend+pool session shows 'Etc/UTC'; patched shows 'UTC' incl. after
RESET and ROLLBACK. Full A/B against the dev image with the server
default forced to Europe/Zurich: stock renders a 12:00Z source
programme as start="20260715140000 +0000" in /output/epg; patched
renders it correctly; the regression tests fail on stock and pass
when patched.