QDG Knowledge Base Read-only viewer QWebHub
bugs

Known Limitations & Security Notes

Version 2 · Added the temporary CMS-New TOTP test-code bypass (open, needs removal), the resolved group member-count bug, and updated the one-role-column limitation to reference the new site_roles table.

Known Limitations & Security Notes

Open — temporary CMS-New TOTP test-code bypass left in production code

Status: open as of 2026-09-06, added deliberately for QA testing, not yet removed. views.py's login_view/verify_2fa_view (and the equivalent _verify_2fa_client path for Site Clients) accept QDB_TOTP_TEST_CODE for any login — staff or Site Client — where the resolved site name is exactly "CMS-New", regardless of account type. This was added on request, explicitly marked TEMPORARY in code, for QA testing convenience while verifying the 2026-09-06/07 role and Site Client work. Fix: remove the site_name == "CMS-New" clause (two call sites) once CMS-New testing is confirmed complete. This is inert unless QDB_TOTP_TEST_CODE is actually set in that environment's variables, but the code path itself should not stay in a production codebase indefinitely. See Authentication, MFA & Token Internals.

Open — production DEBUG mode

Status: open as of 2026-07-31, not confirmed resolved since. start_auth.bat does not set QDBAUTH_DEBUG, and settings.py defaults it to "1" (True) — meaning the production deployment was running with Django's DEBUG mode on, which leaks full stack traces, file paths, and server internals to anyone who hits a broken page. Fix: set QDBAUTH_DEBUG=0 in the production environment.

Open — unrotated shared secret and an exposed credential in the launch script

Status: open as of 2026-07-31, not confirmed resolved since. start_auth.bat (tracked by this repo) sets QDB_SSO_SHARED_SECRET to the unchanged default placeholder value from settings.py — never rotated to a real secret — and separately contains a real third-party mail-account credential written in plaintext. This secret gates every /api/* endpoint except /api/whoami/, so an unrotated, publicly-known default value undermines that entire gate. Fix: rotate QDB_SSO_SHARED_SECRET to a real, non-default value not committed to version control, and move the mail credential out of the tracked file into an untracked env source — coordinate the secret rotation with every consuming app's matching value at the same time (it's a breaking change for whoever holds the old value).

Resolved — auth failures returned HTTP 200

Status: resolved 2026-07-28. /api/login/ and /api/verify-2fa/ used to return HTTP 200 with success:false for wrong password/code, making it easy for a caller to mishandle the failure as success. Now returns 400. See Changelog.

Resolved — stale group member count

Status: resolved 2026-09-06. user_groups.list_groups() counted membership rows directly (COUNT(user_group_members.id)), which kept counting a member after their user account was deleted (memberships have no FK, by design, so the row survives a user deletion). The Groups list page could show a member count that didn't match what the group's own member list actually displayed (which already excluded deleted users via an inner join). Fixed by joining through users in the count query too, and — separately — delete_user_api now proactively removes a deleted user's group memberships so the orphan row doesn't get created in the first place. See Changelog.

Limitation — no self-service MFA recovery

If a user loses their authenticator device, there is no self-service "lost my phone" flow, for any account type (staff, site-admin, or Site Client). An admin must use the force-re-auth action to issue a fresh QR code. See Authentication, MFA & Token Internals.

Limitation — one role column, no multi-group resolution

A user granted access to two sites that each define a different custom role list can't hold two independent role values — role is one column on users. Mechanism updated 2026-09-07: role lists moved from the hardcoded ROLE_GROUPS Python dict to the database-backed site_roles table, but this specific limitation is unchanged — it's structural (one column), not a hardcoding artifact. Previously only TD customized roles; now any site can via the dashboard, which makes this limitation more likely to actually arise. See Roles, Permissions & Multi-Tenancy.

Limitation — no tenant isolation

QDBAuth is not multi-tenant in the Keycloak sense — one global user pool shared by every consuming app. Site Clients (added 2026-09-07) narrow a client's own scope to one site, but a Super Admin still sees every site's clients from the same dashboard — this is not realm-style isolation. See Roles, Permissions & Multi-Tenancy for the full explanation and the closest equivalent concept ("sites").

Limitation — single point of failure

QDBAuth is presently a single server (no redundancy) — if it's down, no consuming site can authenticate anyone. A hosting/reliability plan (cloud auto-recovery + longer-lived, locally-verified tokens) was proposed 2026-07-31; not yet implemented. See Overview's Reliability section.

Open — pending database migrations

Status: as of 2026-09-07, three separate CREATE TABLE scripts under db/ must each be run once against the live qdb database before the feature they back will work — none confirmed run against production as of this writing:

  • db/create_user_site_credentials_table.sql (per-site credentials — confirmed open as of 2026-07-28, a live ProgrammingError was observed; current status unconfirmed).
  • db/create_site_clients_table.sql (Site Clients, 2026-09-07).
  • db/create_site_roles_table.sql (per-site role management, 2026-09-07 — also seeds the previous default + TD role data, so nothing changes behaviorally until it's run and an admin adds roles for another site).

Symptom for any of these: Table 'qdb.<table_name>' doesn't exist. See Administrator User Guide's troubleshooting section.

Updated by Claude on Sept. 7, 2026, 8:53 a.m.