SPEC 019 — Admin Panel
| Field | Value |
|---|---|
| Status | DRAFT |
| Priority | P1 — Core Product |
| Backend | equa-server/modules/admin/ |
| Frontend | equa-web/src/modules/admin/ |
1. Feature Purpose
The Admin Panel provides site-level administrators with tools to manage users, view platform statistics, and enforce domain/email blacklists. Unlike organization-level roles (SPEC 012), admin access is granted through theGlobalRolesUsers table and operates across all organizations on the platform. This is the control plane for platform operators to monitor health, manage abuse, and support users.
2. Current State (Verified)
2.1 Global Role System
| Detail | Value |
|---|---|
| Entity | GlobalRolesUsers — maps users to site-level roles |
| Role field | role (GlobalRole integer enum) |
| Guard | Middleware checks GlobalRolesUsers before allowing admin endpoint access |
| Scope | Admin privileges are platform-wide, not scoped to any organization |
2.2 User Management
| Detail | Value |
|---|---|
| Capabilities | List all users, search by email, enable/disable accounts, view user details |
| Backend | equa-server/modules/admin/ handles user queries and mutations |
| Endpoints | equa-server/modules/api/src/endpoints/admin-endpoints.ts |
2.3 Site Statistics
| Detail | Value |
|---|---|
| Metrics | Total users, organizations, active sessions, recent sign-ups, storage usage |
| Source | Aggregation queries against Users, Organizations, Sessions tables |
| Display | Dashboard cards and charts on the admin frontend |
2.4 Blacklists
| Detail | Value |
|---|---|
| Domain blacklist | DomainBlacklists — blocks registration from specific email domains |
| Email blacklist | EmailBlacklists — blocks specific email addresses |
| Enforcement | Checked during registration and invitation flows |
2.5 Frontend
| Component | Path |
|---|---|
| Admin module | equa-web/src/modules/admin/ |
| Dashboard | Admin landing page with stats cards |
| User management | Searchable user table with action menus |
3. Data Model
GlobalRolesUsers
| Column | Type | Constraints |
|---|---|---|
| user | uuid | PK, FK → Users |
| role | integer | NOT NULL — GlobalRole enum value |
DomainBlacklists
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| domain | varchar | UNIQUE, NOT NULL — e.g. spam-domain.com |
| reason | varchar | nullable |
| createdBy | uuid | FK → Users |
| createdAt | timestamp | DEFAULT now() |
EmailBlacklists
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| citext | UNIQUE, NOT NULL | |
| reason | varchar | nullable |
| createdBy | uuid | FK → Users |
| createdAt | timestamp | DEFAULT now() |
4. API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/admin/stats | Admin | Platform-wide statistics (users, orgs, sessions, storage) |
| GET | /api/v1/admin/users | Admin | Paginated user list with search and filters |
| GET | /api/v1/admin/users/:userId | Admin | Single user detail (orgs, sessions, activity) |
| PUT | /api/v1/admin/users/:userId | Admin | Update user (enable/disable, modify fields) |
| DELETE | /api/v1/admin/users/:userId | Admin | Soft-delete or permanently remove a user account |
| GET | /api/v1/admin/organizations | Admin | Paginated organization list |
| GET | /api/v1/admin/blacklists/domains | Admin | List domain blacklist entries |
| POST | /api/v1/admin/blacklists/domains | Admin | Add a domain to the blacklist |
| DELETE | /api/v1/admin/blacklists/domains/:id | Admin | Remove a domain from the blacklist |
| GET | /api/v1/admin/blacklists/emails | Admin | List email blacklist entries |
| POST | /api/v1/admin/blacklists/emails | Admin | Add an email to the blacklist |
| DELETE | /api/v1/admin/blacklists/emails/:id | Admin | Remove an email from the blacklist |
GlobalRolesUsers with an appropriate admin role.
5. Frontend Components
| Component | Path | Description |
|---|---|---|
| AdminDashboard | admin/AdminDashboard.tsx | Landing page with statistics cards (users, orgs, sessions, storage) |
| UserManagement | admin/UserManagement.tsx | Searchable, paginated user table with inline actions |
| UserDetail | admin/UserDetail.tsx | Full user profile view with organization memberships and session history |
| DomainBlacklist | admin/DomainBlacklist.tsx | CRUD table for blocked domains |
| EmailBlacklist | admin/EmailBlacklist.tsx | CRUD table for blocked email addresses |
| OrgBrowser | admin/OrgBrowser.tsx | Paginated organization list with summary stats |
Frontend Behavior
- Route guard — Admin routes are protected by a global role check; non-admins see a 403 page or are redirected.
- Search — User management supports real-time search by email, username, or user ID with debounced API calls.
- Bulk actions — User list supports multi-select for bulk enable/disable operations.
- Statistics refresh — Dashboard stats auto-refresh on a 60-second interval while the page is visible.
- Confirmation modals — Destructive actions (disable user, delete blacklist entry) require confirmation before execution.
6. Business Rules
- Global role separation — Admin access is completely separate from organization-level roles. Being an org admin does not grant platform admin access.
- GlobalRolesUsers is the single source of truth — All admin middleware checks this table; there is no fallback to environment variables or hardcoded user IDs.
- Domain blacklist enforcement — During registration and team invitation, the email domain is checked against
DomainBlacklists; matching domains are rejected with a generic error (no leak of blacklist existence). - Email blacklist enforcement — Specific email addresses in
EmailBlacklistsare blocked from registration and invitation with the same generic error. - Blacklist is case-insensitive — Domain comparison is lowercase; email comparison uses
citextfor case-insensitive matching. - User disable vs delete — Disabling a user (
enabled = false) blocks login but preserves data. Deletion removes the account and is irreversible. - Audit trail — Blacklist entries record
createdByto track which admin added them. - Self-protection — Admins cannot disable or delete their own account through the admin panel.
- No self-promotion — Adding entries to
GlobalRolesUsersis restricted to existing admins or direct database access; there is no self-service admin promotion endpoint. - Statistics are real-time — Stats endpoints run live aggregation queries; no materialized views or caches are used.
7. Acceptance Criteria
- Only users with a
GlobalRolesUsersentry can access admin endpoints and routes - Non-admin users receive 403 on admin API calls and are redirected on frontend
- Admin dashboard displays correct counts for users, organizations, and active sessions
- User management table supports pagination and search by email/username
- Admin can disable a user account; disabled user cannot log in
- Admin can re-enable a disabled user account
- Admin cannot disable their own account
- Admin can add a domain to the blacklist; new registrations from that domain are blocked
- Admin can add a specific email to the blacklist; that email cannot register or be invited
- Blacklist checks are case-insensitive
- Blacklist rejections show generic error messages (no information leak)
- Admin can remove blacklist entries; previously blocked domains/emails can register again
- Organization browser displays all organizations with summary information
- Destructive actions require confirmation modal
8. Risks
| Risk | Impact | Mitigation |
|---|---|---|
Privilege escalation via direct DB insert into GlobalRolesUsers | Unauthorized admin access | Restrict DB write access; audit GlobalRolesUsers changes |
| Admin account compromise | Full platform control | Require 2FA for admin accounts; log all admin actions |
| Real-time stats queries on large datasets | Slow dashboard load, DB strain | Add query timeouts; consider materialized views if scale demands |
Blacklist bypass via email aliases (e.g. user+tag@domain.com) | Blocked users re-register | Normalize emails (strip + aliases) before blacklist check |
| Accidental user deletion | Irreversible data loss | Implement soft-delete with grace period before hard purge |
| No rate limiting on admin endpoints | Abuse by compromised admin session | Add rate limiting; log and alert on unusual admin activity patterns |
| Blacklist reason field is optional | Poor audit trail for why domains/emails were blocked | Consider making reason required or defaulting to a standard message |
| Self-protection rule bypass via API | Admin disables own account, locks themselves out | Server-side check preventing self-targeting on disable/delete endpoints |