.. include:: ../variables.rst .. _challenges-permissions: Permissions and Roles ===================== Who can do what in the Challenges module is governed by the same role system as the rest of |APPNAME| -- the ``roles`` table with its ``roletype`` column (0 = Undefined, 1 = Standard, 2 = Administrator, 3 = Demo). .. figure:: /images/challenges/lightmode/permissions-role-table.png :align: center :width: 90 % |br| Who can do what --------------- .. list-table:: :header-rows: 1 :widths: 45 15 15 15 10 * - Action - Administrator - Standard - Demo - Anonymous * - View challenges list / results / ledger - Yes - Yes - Yes - No * - View participant profile - Yes - Yes - Yes - No * - Create / edit / delete challenges - Yes - No - No - No * - Compute challenge - Yes - Yes [#]_ - No - No * - Preview close - Yes - Yes - No - No * - Close challenge - Yes - No - No - No * - Clone challenge - Yes - No - No - No * - Create / edit badges - Yes - No - No - No * - Delete (soft) badge - Yes - No - No - No * - Redeem / adjust points - Yes - No - No - No * - Manage teams - Yes - No - No - No * - Manage badge auto-rules - Yes - No - No - No * - Export CSV - Yes - Yes - Yes - No .. [#] Compute is technically a mutation (it rewrites ``challenge_results``) but is gated more loosely because it is non-destructive outside the challenge itself. How the guard works ------------------- Mutating endpoints can be gated with the ``?userid=`` query parameter. When present, the server looks up ``users.role`` ▸ ``roles.roletype`` and denies the request with HTTP 403 unless the user is an administrator (``roletype = 2``). If ``userid`` is omitted, the request is **allowed** (fail-open). This preserves the historical behaviour of the API and lets server-to-server calls (cron, automation) operate without a user context. Current guard coverage ---------------------- At the time of writing, the role guard is wired onto: * ``POST /challenges`` (create) Additional mutations (update, delete, close, clone, redeem, team CRUD, badge CRUD) rely on the existing frontend gating (sidebar links hidden for non-admins). Tightening the backend guard on those endpoints is a planned follow-up. Rule filter and scope safety ---------------------------- The rule filter (:ref:`challenges-rule-filter`) is sanitised server-side: every comma-list value is reduced to integers before being embedded in SQL. Even a malicious user who could POST to the challenge endpoints could not break out of the intended query shape. Fail-open fallback ------------------ If the ``users`` or ``roles`` tables are unreadable (for example during an upgrade window where the schema is mid-migration), the guard falls back to allow. This is deliberate: locking administrators out of their own system during a schema update is worse than briefly widening access. Auditing -------- Challenge mutations do not currently write to a dedicated audit log. Every state change is, however, recorded in one of the following: * ``challenges`` -- the ``creationdate`` column times the create. * ``challenge_results`` -- the ``computedat`` column times every compute. * ``challenge_points_ledger`` -- every point award / spend / expire / adjust, with a timestamp and optional notes. * ``challenge_badges_awarded`` -- every badge grant with a timestamp. Taken together these form an effective audit trail for the module's data without needing a separate log.