diff --git a/CLAUDE.md b/CLAUDE.md index 34bd686..4abdddb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -553,3 +553,76 @@ const params = new URLSearchParams({ **QSO Management**: - Fixed DELETE /api/qsos/all to handle foreign key constraints - Added cache invalidation after QSO deletion + +### Admin System and User Roles + +The application supports three user roles with different permission levels: + +**User Roles**: +- **Regular User**: View own QSOs, sync from LoTW/DCL, track award progress +- **Admin**: All user permissions + view system stats + manage users + impersonate regular users +- **Super Admin**: All admin permissions + promote/demote admins + impersonate admins + +**Database Schema** (`src/backend/db/schema/index.js`): +- `isAdmin`: Boolean flag for admin users (default: false) +- `isSuperAdmin`: Boolean flag for super-admin users (default: false) + +**Admin Service** (`src/backend/services/admin.service.js`): +- `isAdmin(userId)`: Check if user is admin +- `isSuperAdmin(userId)`: Check if user is super-admin +- `changeUserRole(adminId, targetUserId, newRole)`: Change user role ('user', 'admin', 'super-admin') +- `impersonateUser(adminId, targetUserId)`: Start impersonating a user +- `verifyImpersonation(token)`: Verify impersonation token validity +- `stopImpersonation(adminId, targetUserId)`: Stop impersonation +- `logAdminAction(adminId, actionType, targetUserId, details)`: Log admin actions + +**Security Rules**: +1. Only super-admins can promote/demote super-admins +2. Regular admins cannot promote users to super-admin +3. Super-admins cannot demote themselves (prevents lockout) +4. Cannot demote the last super-admin +5. Regular admins can only impersonate regular users +6. Super-admins can impersonate any user (including other admins) + +**Backend API Routes** (`src/backend/index.js`): +- `POST /api/admin/users/:userId/role`: Change user role + - Body: `{ "role": "user" | "admin" | "super-admin" }` +- `POST /api/admin/impersonate/:userId`: Start impersonating +- `POST /api/admin/impersonate/stop`: Stop impersonating +- `GET /api/admin/impersonation/status`: Check impersonation status +- `GET /api/admin/stats`: System statistics +- `GET /api/admin/users`: List all users +- `GET /api/admin/actions`: Admin action log +- `DELETE /api/admin/users/:userId`: Delete user + +**JWT Token Claims**: +```javascript +{ + userId: number, + email: string, + callsign: string, + isAdmin: boolean, + isSuperAdmin: boolean, // Super-admin flag + impersonatedBy: number, // Present when impersonating + exp: number +} +``` + +**Frontend Admin Page** (`src/frontend/src/routes/admin/+page.svelte`): +- System statistics dashboard +- User management with filtering (all, super-admin, admin, user) +- Role change modal (user → admin → super-admin) +- Impersonate button (enabled for super-admins targeting admins) +- Admin action log viewing + +**To create the first super-admin**: +1. Register a user account +2. Access database: `sqlite3 src/backend/award.db` +3. Run: `UPDATE users SET is_super_admin = 1 WHERE email = 'your@email.com';` +4. Log out and log back in to get updated JWT token + +**To promote via admin interface**: +1. Log in as existing super-admin +2. Navigate to `/admin` +3. Find user in Users tab +4. Click "Promote" and select "Super Admin" diff --git a/README.md b/README.md index 6843f1f..d6e498e 100644 --- a/README.md +++ b/README.md @@ -277,6 +277,52 @@ The application will be available at: ### Health - `GET /api/health` - Health check endpoint +### Admin API (Admin Only) + +All admin endpoints require authentication and admin privileges. + +- `GET /api/admin/stats` - Get system-wide statistics +- `GET /api/admin/users` - Get all users with statistics +- `GET /api/admin/users/:userId` - Get detailed information about a specific user +- `POST /api/admin/users/:userId/role` - Update user role (`user`, `admin`, `super-admin`) +- `DELETE /api/admin/users/:userId` - Delete a user +- `POST /api/admin/impersonate/:userId` - Start impersonating a user +- `POST /api/admin/impersonate/stop` - Stop impersonating and return to admin account +- `GET /api/admin/impersonation/status` - Get current impersonation status +- `GET /api/admin/actions` - Get admin actions log +- `GET /api/admin/actions/my` - Get current admin's action log + +### User Roles and Permissions + +The application supports three user roles with different permission levels: + +**Regular User** +- View own QSOs +- Sync from LoTW and DCL +- Track award progress +- Manage own credentials + +**Admin** +- All user permissions +- View system statistics +- View all users +- Promote/demote regular users to/from admin +- Delete regular users +- Impersonate regular users (for support) +- View admin action log + +**Super Admin** +- All admin permissions +- Promote/demote admins to/from super-admin +- Impersonate other admins (for support) +- Full access to all admin functions + +**Security Rules:** +- Only super-admins can promote or demote super-admins +- Regular admins cannot promote users to super-admin +- Super-admins cannot demote themselves +- Cannot demote the last super-admin + ## Database Schema ### Users Table @@ -289,6 +335,9 @@ CREATE TABLE users ( lotwUsername TEXT, lotwPassword TEXT, dclApiKey TEXT, -- DCL API key (for future use) + isAdmin INTEGER DEFAULT 0 NOT NULL, + isSuperAdmin INTEGER DEFAULT 0 NOT NULL, + lastSeen INTEGER, createdAt TEXT NOT NULL, updatedAt TEXT NOT NULL ); diff --git a/docs/DOCUMENTATION.md b/docs/DOCUMENTATION.md index fd08486..52242eb 100644 --- a/docs/DOCUMENTATION.md +++ b/docs/DOCUMENTATION.md @@ -276,6 +276,9 @@ award/ callsign: text (not null) lotwUsername: text (nullable) lotwPassword: text (nullable, encrypted) + isAdmin: boolean (default: false) + isSuperAdmin: boolean (default: false) + lastSeen: timestamp (nullable) createdAt: timestamp updatedAt: timestamp } @@ -1034,7 +1037,197 @@ When adding new awards or modifying the award system: --- -## Resources +## Admin System + +### Overview + +The admin system provides user management, role-based access control, and account impersonation capabilities for support and administrative purposes. + +### User Roles + +The application supports three user roles with increasing permissions: + +#### Regular User +- View own QSOs and statistics +- Sync from LoTW and DCL +- Track award progress +- Manage own credentials (LoTW, DCL) + +#### Admin +- All user permissions +- View system-wide statistics +- View all users and their activity +- Promote/demote regular users to/from admin role +- Delete regular users +- Impersonate regular users (for support) +- View admin action log + +#### Super Admin +- All admin permissions +- Promote/demote admins to/from super-admin role +- Impersonate other admins (for support) +- Cannot be demoted by regular admins +- Protected from accidental lockout + +### Security Rules + +**Role Change Restrictions:** +- Only super-admins can promote or demote super-admins +- Regular admins cannot promote users to super-admin +- Super-admins cannot demote themselves +- Cannot demote the last super-admin (prevents lockout) + +**Impersonation Restrictions:** +- Regular admins can only impersonate regular users +- Super-admins can impersonate any user (including other admins) +- All impersonation actions are logged to audit trail +- Impersonation tokens expire after 1 hour + +### Admin API Endpoints + +**Statistics and Monitoring:** +- `GET /api/admin/stats` - System-wide statistics (users, QSOs, jobs) +- `GET /api/admin/users` - List all users with statistics +- `GET /api/admin/users/:userId` - Get detailed user information +- `GET /api/admin/actions` - View admin action log +- `GET /api/admin/actions/my` - View current admin's actions + +**User Management:** +- `POST /api/admin/users/:userId/role` - Change user role + - Body: `{ "role": "user" | "admin" | "super-admin" }` +- `DELETE /api/admin/users/:userId` - Delete a user + +**Impersonation:** +- `POST /api/admin/impersonate/:userId` - Start impersonating a user +- `POST /api/admin/impersonate/stop` - Stop impersonation +- `GET /api/admin/impersonation/status` - Check impersonation status + +### Admin Service + +**File:** `src/backend/services/admin.service.js` + +**Key Functions:** + +```javascript +// Check user permissions +await isAdmin(userId) +await isSuperAdmin(userId) + +// Role management +await changeUserRole(adminId, targetUserId, newRole) + +// Impersonation +await impersonateUser(adminId, targetUserId) +await verifyImpersonation(impersonationToken) +await stopImpersonation(adminId, targetUserId) + +// Audit logging +await logAdminAction(adminId, actionType, targetUserId, details) +``` + +### Audit Logging + +All admin actions are logged to the `admin_actions` table for audit purposes: + +**Action Types:** +- `impersonate_start` - Started impersonating a user +- `impersonate_stop` - Stopped impersonation +- `role_change` - Changed user role +- `user_delete` - Deleted a user + +**Log Entry Structure:** +```javascript +{ + id: integer, + adminId: integer, + actionType: string, + targetUserId: integer (nullable), + details: string (JSON), + createdAt: timestamp +} +``` + +### Frontend Admin Interface + +**Route:** `/admin` (admin only) + +**Features:** +- **Overview Tab:** System statistics dashboard +- **Users Tab:** User management with filtering +- **Awards Tab:** Award definition management +- **Action Log Tab:** Audit trail of admin actions + +**User Management Actions:** +- **Impersonate** - Switch to user account (disabled for admins unless super-admin) +- **Promote/Demote** - Change user role +- **Delete** - Remove user and all associated data + +### JWT Token Claims + +Admin tokens include additional claims: + +```javascript +{ + userId: number, + email: string, + callsign: string, + isAdmin: boolean, + isSuperAdmin: boolean, // New: Super-admin flag + exp: number +} +``` + +**Impersonation Token:** +```javascript +{ + userId: number, // Target user ID + email: string, + callsign: string, + isAdmin: boolean, + isSuperAdmin: boolean, + impersonatedBy: number, // Admin ID who started impersonation + exp: number // 1 hour expiration +} +``` + +### Setup + +**To create the first super-admin:** + +1. Register a user account normally +2. Access the database directly: +```bash +sqlite3 src/backend/award.db +``` + +3. Update the user to super-admin: +```sql +UPDATE users SET is_super_admin = 1 WHERE email = 'your@email.com'; +``` + +4. Log out and log back in to get the updated JWT token + +**To promote users via the admin interface:** +1. Log in as a super-admin +2. Navigate to `/admin` +3. Find the user in the Users tab +4. Click "Promote" and select "Super Admin" + +### Production Deployment + +After pulling the latest code: + +```bash +# Apply database migration (adds is_super_admin column) +sqlite3 src/backend/award.db "ALTER TABLE users ADD COLUMN is_super_admin INTEGER DEFAULT 0 NOT NULL;" + +# Restart backend +pm2 restart award-backend + +# Promote a user to super-admin via database or existing admin interface +``` + +--- - [ARRL LoTW](https://lotw.arrl.org/) - [DARC Community Logbook (DCL)](https://dcl.darc.de/)