Files
HIPCTF2/docs/database/users.md
T

53 lines
2.1 KiB
Markdown

---
type: database
title: User Table
description: The `user` table — accounts, roles, and statuses.
tags: [database, user, auth]
timestamp: 2026-07-21T14:43:00Z
---
# Schema
| Column | Type | Description |
|----------------|----------|------------------------------------------------------------------|
| `id` | TEXT PK | UUID v4 generated on creation. |
| `username` | TEXT | Unique login name. |
| `password_hash`| TEXT | Argon2id hash (memory/time/parallelism from env config). |
| `role` | TEXT | `'admin'` or `'player'`. Default `'player'`. |
| `status` | TEXT | `'enabled'` or `'disabled'`. Default `'enabled'`. |
| `created_at` | TEXT | ISO 8601 timestamp (`strftime('%Y-%m-%dT%H:%M:%fZ','now')`). |
# Constraints
- Unique on `username`.
- `role` is enforced application-side (see `AdminService.updateUserRole`).
- `status` is checked by `AuthService.login` and `AuthService.refresh`
— disabled users cannot log in or refresh.
# Invariants
- The application enforces the **last-admin invariant**: at least one
enabled user with `role = 'admin'` must always exist. Attempts to
demote or delete the last admin are rejected by
`UsersService.enforceLastAdminInvariant` (used by
`AdminService.updateUserRole` and `AdminService.deleteUser`).
# First-admin bootstrap
When the `user` table is empty of admins, the dedicated
`POST /api/v1/setup/create-admin` endpoint (see
[Setup Endpoint](/api/setup.md)) creates the very first admin and
auto-issues a session. Once any admin row exists the endpoint becomes
inert and returns `404 NOT_FOUND`, so the route is effectively hidden
in production.
The legacy `POST /api/v1/auth/register-first-admin` route is preserved
for compatibility but the canonical first-admin flow now lives in the
`SetupModule`.
# See also
- [Auth and Settings Tables](/database/auth-settings.md)
- [REST API Overview](/api/rest-overview.md)
- [Admin Endpoints](/api/admin.md)