Skip to content

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_admin Boolean — the master switch.
  • AdminCredentials 1:1 side table — separate password hash for the admin login path.
  • audit_logs table — HTTP-trail audit log (method/path/status/duration/security_flags/violation), free-form action: 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 (MFASecret 1:1 with User); 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.
  • AdminRestriction types (TIME_LIMIT / IP_WHITELIST / ACTION_LIMIT / RESOURCE_LIMIT): dropped.
  • SecurityAlert table: dropped (SecurityDashboard.tsx reads from audit logs, not from a dedicated alerts table).
  • AccessControl per-admin policy table: dropped.
  • AdminActionType enum: replaced by free-form audit_logs.action String.

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.

  • 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.pyUser.is_admin.
  • services/gameserver/src/models/admin_credentials.py — admin password side table.
  • services/gameserver/src/models/audit_log.py — HTTP-trail audit log.