0027 — ADM1: Flat is_admin + Per-resource Moderation Flags Canonical; RBAC Dropped¶
Status¶
Superseded by 0058
Context¶
Legacy data-models/admin/admin_permissions.md specified an aspirational RBAC system: a 40+-entry AdminPermission enum (galaxy / players / economy / combat / content / system permission categories), 5 predefined roles (Super-Admin, Game Master, Economy Manager, Player Support, Read-Only Analyst), AdminRestriction types (TIME_LIMIT, IP_WHITELIST, ACTION_LIMIT, RESOURCE_LIMIT), per-admin IP allowlists, per-role MFA enforcement, a ~25-entry AdminActionType enum, a SecurityAlert table with severity tiers, and an AccessControl table for per-admin policies.
None of this exists in code. Code reality (models/user.py, models/admin_credentials.py, models/audit_log.py):
- Flat
User.is_adminBoolean — the master switch. AdminCredentials1:1 side table — separate password hash for the admin login path.audit_logstable — HTTP-trail audit log (method/path/status/duration/security_flags/violation), free-formaction: String(100), no before/after diff, no fixed enum.- Per-resource moderation columns:
MarketTransaction.flagged_suspicious / reviewed_by,CombatLog.admin_resolved / admin_reviewed / disputed,Message.flagged / moderated_by,PriceAlert.acknowledged_by,Reputation.is_locked / lock_reason / lock_expires. - User-level MFA exists (
MFASecret1:1 withUser); not role-gated, not admin-only. - Admin-UI scaffolding components (
PermissionsDashboard.tsx,permissions/PermissionMatrix.tsx,permissions/RoleManagement.tsx) render mock fallback data — no backend wiring.
Decision¶
Flat User.is_admin + per-resource moderation flags is canonical at launch. The legacy RBAC surface is dropped:
- 40+-entry permission enum: dropped.
- 5 predefined roles: dropped.
AdminRestrictiontypes (TIME_LIMIT / IP_WHITELIST / ACTION_LIMIT / RESOURCE_LIMIT): dropped.SecurityAlerttable: dropped (SecurityDashboard.tsxreads from audit logs, not from a dedicated alerts table).AccessControlper-admin policy table: dropped.AdminActionTypeenum: replaced by free-formaudit_logs.actionString.
The PermissionMatrix / RoleManagement UI components remain in the admin-UI repo as scaffolding for a possible future RBAC migration; they are 📐 Design-only at launch and explicitly marked as such in OPERATIONS/admin-ui.md.
RBAC is a meaningful operational complexity layer that earns its weight only when the admin headcount or the action-policy granularity demands it. At launch — single-digit admins, all trusted, performing actions the audit log already captures — the flat is_admin boolean plus per-resource moderation flags covers the surface without requiring a permissions table, role hierarchy, grant table, restriction enforcement layer, or MFA-by-role gates. The trade-off is admitted: any admin with is_admin=True can do anything the admin endpoints expose; mitigation is the audit log, the soft-delete preservation of player data, and the per-resource lock columns (Reputation.is_locked, User.is_active) that act as targeted overrides without requiring an "unlock-rep-only" role.
This decision is recorded so future contributors don't re-propose RBAC from first principles. If a future round needs role-based gating (e.g., third-party support contractors get read-only access without is_admin), the design starts from the existing AdminCredentials row plus a small admin_role enum column — not a re-import of the legacy 40-permission matrix.
Consequences¶
Positive:
- Launch-scope admin surface is a small, legible set of columns and tables — no permissions matrix, no grant graph, no restriction-enforcement middleware to maintain.
- The audit log captures every admin action as an HTTP trail with security flags, providing accountability without a fixed AdminActionType enum that would need to be revised every time a new admin endpoint lands.
- Per-resource moderation flags (lock-rep, flag-message, admin-resolve-combat) provide targeted operational overrides without needing role-scoped permissions.
- The admin-UI scaffolding can stay as 📐 Design-only stubs, ready to be wired up if RBAC is later needed.
Neutral:
- User-level MFA still exists via MFASecret; it just isn't role-gated. Admins can opt in to MFA without RBAC infrastructure.
- DATA_MODELS/admin.md is already canonical — flat boolean plus per-resource flags are documented.
Negative:
- Any is_admin=True user can do anything the admin endpoints expose. This is acceptable at launch given a small trusted admin pool, but is the explicit trade-off this ADR makes.
- Third-party or read-only-support roles cannot be added at launch without code changes; the path forward (small admin_role enum on AdminCredentials) is named here so the eventual migration starts from the right foundation.
Related docs¶
DATA_MODELS/admin.md— flat boolean and per-resource moderation flags (canonical).OPERATIONS/admin-ui.md— Security & audit section (RBAC UI shells marked 📐 Design-only with cross-link here); Admin REST rate-limits subsection (📐 Design-only).services/gameserver/src/models/user.py—User.is_admin.services/gameserver/src/models/admin_credentials.py— admin password side table.services/gameserver/src/models/audit_log.py— HTTP-trail audit log.