feat: Implement centralized audit logging admin interface (refs #10)
Some checks failed
Deploy to Staging / Build Images (pull_request) Successful in 4m42s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 37s
Deploy to Staging / Verify Staging (pull_request) Failing after 6s
Deploy to Staging / Notify Staging Ready (pull_request) Has been skipped
Deploy to Staging / Notify Staging Failure (pull_request) Successful in 6s

- Add audit_logs table with categories, severities, and indexes
- Create AuditLogService and AuditLogRepository
- Add REST API endpoints for viewing and exporting logs
- Wire audit logging into auth, vehicles, admin, and backup features
- Add desktop AdminLogsPage with filters and CSV export
- Add mobile AdminLogsMobileScreen with card layout
- Implement 90-day retention cleanup job
- Remove old AuditLogPanel from AdminCatalogPage

Security fixes:
- Escape LIKE special characters to prevent pattern injection
- Limit CSV export to 5000 records to prevent memory exhaustion
- Add truncation warning headers for large exports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-11 11:09:09 -06:00
parent 8c7de98a9a
commit c98211f4a2
30 changed files with 2897 additions and 11 deletions

View File

@@ -6,6 +6,7 @@
import { AdminRepository } from '../data/admin.repository';
import { AdminUser, AdminAuditLog } from './admin.types';
import { logger } from '../../../core/logging/logger';
import { auditLogService } from '../../audit-log';
export class AdminService {
constructor(private repository: AdminRepository) {}
@@ -58,12 +59,22 @@ export class AdminService {
// Create new admin
const admin = await this.repository.createAdmin(auth0Sub, normalizedEmail, role, createdBy);
// Log audit action
// Log audit action (legacy)
await this.repository.logAuditAction(createdBy, 'CREATE', admin.auth0Sub, 'admin_user', admin.email, {
email,
role
});
// Log to unified audit log
await auditLogService.info(
'admin',
createdBy,
`Admin user created: ${admin.email}`,
'admin_user',
admin.auth0Sub,
{ email: admin.email, role }
).catch(err => logger.error('Failed to log admin create audit event', { error: err }));
logger.info('Admin user created', { email, role });
return admin;
} catch (error) {
@@ -83,9 +94,19 @@ export class AdminService {
// Revoke the admin
const admin = await this.repository.revokeAdmin(auth0Sub);
// Log audit action
// Log audit action (legacy)
await this.repository.logAuditAction(revokedBy, 'REVOKE', auth0Sub, 'admin_user', admin.email);
// Log to unified audit log
await auditLogService.info(
'admin',
revokedBy,
`Admin user revoked: ${admin.email}`,
'admin_user',
auth0Sub,
{ email: admin.email }
).catch(err => logger.error('Failed to log admin revoke audit event', { error: err }));
logger.info('Admin user revoked', { auth0Sub, email: admin.email });
return admin;
} catch (error) {
@@ -99,9 +120,19 @@ export class AdminService {
// Reinstate the admin
const admin = await this.repository.reinstateAdmin(auth0Sub);
// Log audit action
// Log audit action (legacy)
await this.repository.logAuditAction(reinstatedBy, 'REINSTATE', auth0Sub, 'admin_user', admin.email);
// Log to unified audit log
await auditLogService.info(
'admin',
reinstatedBy,
`Admin user reinstated: ${admin.email}`,
'admin_user',
auth0Sub,
{ email: admin.email }
).catch(err => logger.error('Failed to log admin reinstate audit event', { error: err }));
logger.info('Admin user reinstated', { auth0Sub, email: admin.email });
return admin;
} catch (error) {