diff --git a/webapp/src/lib/demo.ts b/webapp/src/lib/demo.ts index ff21514..f1513d4 100644 --- a/webapp/src/lib/demo.ts +++ b/webapp/src/lib/demo.ts @@ -9,6 +9,7 @@ import type { import type { AdminInvite, AdminUser, + AuditLogEntry, AuthorizedDevice, Cipher, Folder, @@ -712,6 +713,233 @@ export const DEMO_BACKUP_SETTINGS: AdminBackupSettings = { ], }; +export const DEMO_AUDIT_LOGS: AuditLogEntry[] = [ + { + id: 'demo-log-auth-login', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'auth.login.success', + category: 'auth', + level: 'info', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ ip: '203.0.113.42', device: 'Chrome 125 on Windows', location: 'San Francisco, US' }), + createdAt: '2026-07-08T14:32:10.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-auth-failed', + actorUserId: null, + actorEmail: 'unknown@example.com', + action: 'auth.login.failed', + category: 'auth', + level: 'warn', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ ip: '198.51.100.7', reason: 'invalid_password', attemptCount: 3 }), + createdAt: '2026-07-08T13:15:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-auth-2fa', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'auth.totp.enabled', + category: 'auth', + level: 'security', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ ip: '203.0.113.42', trigger: 'user_initiated' }), + createdAt: '2026-07-07T09:00:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-auth-refresh-failed', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'auth.refresh.failed.token_expired', + category: 'auth', + level: 'error', + targetType: null, + targetId: 'demo-device-browser', + targetUserEmail: null, + metadata: JSON.stringify({ ip: '203.0.113.42', device: 'Chrome 125 on Windows' }), + createdAt: '2026-07-06T18:45:30.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-security-password', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'user.password.changed', + category: 'security', + level: 'security', + targetType: 'user', + targetId: DEMO_USER_ID, + targetUserEmail: DEMO_PROFILE.email, + metadata: JSON.stringify({ ip: '203.0.113.42', trigger: 'user_initiated' }), + createdAt: '2026-07-05T10:00:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-security-user-banned', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'admin.user.banned', + category: 'security', + level: 'security', + targetType: 'user', + targetId: 'demo-user-003', + targetUserEmail: 'suspended@example.com', + metadata: JSON.stringify({ ip: '203.0.113.42', reason: 'violation_of_tos' }), + createdAt: '2026-07-04T16:20:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-security-user-register', + actorUserId: null, + actorEmail: 'newuser@example.com', + action: 'user.register.completed', + category: 'security', + level: 'info', + targetType: 'user', + targetId: 'demo-user-004', + targetUserEmail: 'newuser@example.com', + metadata: JSON.stringify({ ip: '192.0.2.55', invite: 'DEMO-INVITE-2026' }), + createdAt: '2026-07-03T08:30:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-device-trusted', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'device.trusted.added', + category: 'device', + level: 'info', + targetType: 'device', + targetId: 'demo-device-mobile', + targetUserEmail: null, + metadata: JSON.stringify({ deviceName: 'iPhone', os: 'iOS 18', ip: '203.0.113.42' }), + createdAt: '2026-07-02T12:15:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-device-removed', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'device.removed', + category: 'device', + level: 'warn', + targetType: 'device', + targetId: 'demo-device-old', + targetUserEmail: null, + metadata: JSON.stringify({ deviceName: 'Firefox on Linux', ip: '198.51.100.20', trigger: 'user_initiated' }), + createdAt: '2026-07-01T09:45:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-device-all-revoked', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'device.all_trust_revoked', + category: 'device', + level: 'security', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ ip: '203.0.113.42', trigger: 'password_change' }), + createdAt: '2026-07-01T09:00:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-data-backup', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'admin.backup.run.completed', + category: 'data', + level: 'info', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ fileName: 'nodewarden_backup_20260701_030000.zip', size: '1.2 MB', destination: 'Demo WebDAV' }), + createdAt: '2026-07-01T03:00:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-data-restore', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'admin.backup.restore.completed', + category: 'data', + level: 'warn', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ fileName: 'nodewarden_backup_20260628_030000.zip', checksum: 'verified' }), + createdAt: '2026-06-30T14:00:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-data-export', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'admin.export.completed', + category: 'data', + level: 'info', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ format: 'encrypted_json', totalItems: 24 }), + createdAt: '2026-06-28T11:30:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-system-settings', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'admin.settings.updated', + category: 'system', + level: 'info', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ changedKeys: ['signupsAllowed', 'kdfIterations'], ip: '203.0.113.42' }), + createdAt: '2026-06-25T08:00:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-system-invite', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'admin.invite.created', + category: 'system', + level: 'info', + targetType: 'invite', + targetId: 'DEMO-INVITE-2026', + targetUserEmail: null, + metadata: JSON.stringify({ expiresIn: '168h', ip: '203.0.113.42' }), + createdAt: '2026-06-20T10:00:00.000Z', + object: 'auditLog', + }, + { + id: 'demo-log-system-config', + actorUserId: DEMO_USER_ID, + actorEmail: DEMO_PROFILE.email, + action: 'admin.config.updated', + category: 'system', + level: 'warn', + targetType: null, + targetId: null, + targetUserEmail: null, + metadata: JSON.stringify({ changedKeys: ['smtp.host', 'smtp.port'], ip: '203.0.113.42' }), + createdAt: '2026-06-18T15:30:00.000Z', + object: 'auditLog', + }, +]; + function cloneJson(value: T): T { return JSON.parse(JSON.stringify(value)) as T; } @@ -1212,6 +1440,38 @@ export function createDemoMainRoutesProps(base: AppMainRoutesProps, notify: Noti onGetRecoveryCode: readonlyString, onGetApiKey: readonlyString, onRotateApiKey: readonlyString, + onListAccountPasskeys: async () => [], + onCreateAccountPasskey: async () => { + await readonly(); + return null; + }, + onEnableAccountPasskeyDirectUnlock: readonly, + onDeleteAccountPasskey: readonly, + onLoadAuditLogs: async (filters) => { + const limit = Number(filters.limit || 50) || 50; + const offset = Number(filters.offset || 0) || 0; + let filtered = DEMO_AUDIT_LOGS.filter((log) => { + if (filters.category && filters.category !== 'all' && log.category !== filters.category) return false; + if (filters.level && filters.level !== 'all' && log.level !== filters.level) return false; + if (filters.q) { + const q = filters.q.toLowerCase(); + if (!log.action.toLowerCase().includes(q) && !(log.actorEmail || '').toLowerCase().includes(q)) return false; + } + if (filters.from && new Date(log.createdAt).getTime() < new Date(filters.from).getTime()) return false; + if (filters.to && new Date(log.createdAt).getTime() > new Date(filters.to).getTime()) return false; + return true; + }); + filtered.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()); + const total = filtered.length; + const sliced = filtered.slice(offset, offset + limit); + return { + logs: sliced, + total, + limit, + offset: offset + sliced.length, + hasMore: offset + sliced.length < total, + }; + }, onLockTimeoutChange: readonlyVoid, onSessionTimeoutActionChange: readonlyVoid, onRefreshAuthorizedDevices: async () => {