feat: add super-admin role with admin impersonation support

Add a new super-admin role that can impersonate other admins. Regular
admins retain all existing permissions but cannot impersonate other
admins or promote users to super-admin.

Backend changes:
- Add isSuperAdmin field to users table with default false
- Add isSuperAdmin() check function to auth service
- Update JWT tokens to include isSuperAdmin claim
- Allow super-admins to impersonate other admins
- Add security rules for super-admin role changes

Frontend changes:
- Display "Super Admin" badge with gradient styling
- Add "Super Admin" option to role change modal
- Enable impersonate button for super-admins targeting admins
- Add "Super Admins Only" filter option

Security rules:
- Only super-admins can promote/demote super-admins
- Regular admins cannot promote users to super-admin
- Super-admins cannot demote themselves
- Cannot demote the last super-admin

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-23 13:32:55 +01:00
parent a5f0e3b96f
commit ed433902d9
9 changed files with 1022 additions and 50 deletions

View File

@@ -10,6 +10,7 @@ import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
* @property {string|null} lotwPassword
* @property {string|null} dclApiKey
* @property {boolean} isAdmin
* @property {boolean} isSuperAdmin
* @property {Date|null} lastSeen
* @property {Date} createdAt
* @property {Date} updatedAt
@@ -24,6 +25,7 @@ export const users = sqliteTable('users', {
lotwPassword: text('lotw_password'), // Encrypted
dclApiKey: text('dcl_api_key'), // DCL API key for future use
isAdmin: integer('is_admin', { mode: 'boolean' }).notNull().default(false),
isSuperAdmin: integer('is_super_admin', { mode: 'boolean' }).notNull().default(false),
lastSeen: integer('last_seen', { mode: 'timestamp' }),
createdAt: integer('created_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull().$defaultFn(() => new Date()),