// SPDX-License-Identifier: AGPL-3.0-or-later import type { AdminAuditCoverageCase, AdminAuditCoverageContext, } from '@app/api/admin/tests/audit_coverage/AdminAuditCoverage'; import { createTestAccount, createTotpSecret, createUniqueEmail, createUniqueUsername, generateTotpCode, type TestAccount, } from '@app/api/auth/tests/AuthTestUtils'; import { createRegistrationResponse, createWebAuthnDevice, type WebAuthnCredentialMetadata, type WebAuthnRegistrationOptions, } from '@app/api/auth/tests/WebAuthnTestUtils'; import {createUserID} from '@app/api/BrandedTypes'; import {Config} from '@app/api/Config'; import {createFriendship} from '@app/api/channel/tests/ChannelTestUtils'; import { getAdminRepository, getInstanceConfigRepository, getUserRepository, } from '@app/api/middleware/ServiceSingletons'; import type {User} from '@app/api/models/User'; import {createBuilder} from '@app/api/test/TestRequestBuilder'; import {AccountIdentityModes} from '@fluxer/constants/src/AccountIdentityConstants'; import {AdminACLs} from '@fluxer/constants/src/AdminACLs'; import {DeletionReasons} from '@fluxer/constants/src/Core'; import {PremiumFlags, UserFlags} from '@fluxer/constants/src/UserConstants'; import {expect, onTestFinished} from 'vitest'; async function loadUser(account: TestAccount): Promise { const user = await getUserRepository().findUnique(createUserID(BigInt(account.userId))); if (!user) { throw new Error(`User ${account.userId} not found`); } return user; } function adminBuilder({harness, admin}: AdminAuditCoverageContext) { return createBuilder(harness, admin.token); } async function enableTotp(context: AdminAuditCoverageContext, account: TestAccount): Promise { const secret = createTotpSecret(); await createBuilder(context.harness, account.token) .post('/users/@me/mfa/totp/enable') .body({secret, code: generateTotpCode(secret), password: account.password}) .execute(); return secret; } export const UserWriteAdminAuditCases: ReadonlyArray = [ { method: 'DELETE', route: '/admin/users/:user_id/relationships', async prepare({harness}) { const target = await createTestAccount(harness); const friend = await createTestAccount(harness); await createFriendship(harness, target, friend); return { request: {path: `/admin/users/${target.userId}/relationships?category=friend`}, expected: { action: 'remove_relationships_by_category', targetType: 'user', targetId: target.userId, metadata: {category: 'friend', removed_count: '1'}, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/relationships/:target_user_id', async prepare({harness}) { const target = await createTestAccount(harness); const friend = await createTestAccount(harness); await createFriendship(harness, target, friend); return { request: { path: `/admin/users/${target.userId}/relationships/${friend.userId}?category=friend`, expectStatus: 204, }, expected: { action: 'remove_relationship', targetType: 'user', targetId: target.userId, metadata: {target_user_id: friend.userId, category: 'friend'}, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/sessions', async prepare({harness}) { const target = await createTestAccount(harness); return { request: {path: `/admin/users/${target.userId}/sessions`}, expected: { action: 'terminate_sessions', targetType: 'user', targetId: target.userId, metadata: {}, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/webauthn-credentials/:credential_id', async prepare(context) { const {harness} = context; const target = await createTestAccount(harness); const secret = await enableTotp(context, target); const device = createWebAuthnDevice(); const registrationOptions = await createBuilder(harness, target.token) .post('/users/@me/mfa/webauthn/credentials/registration-options') .body({mfa_method: 'totp', mfa_code: generateTotpCode(secret)}) .execute(); if (registrationOptions.rp.id) { device.rpId = registrationOptions.rp.id; } await createBuilder(harness, target.token) .post('/users/@me/mfa/webauthn/credentials') .body({ response: createRegistrationResponse(device, registrationOptions, 'Coverage Passkey'), challenge: registrationOptions.challenge, name: 'Coverage Passkey', mfa_method: 'totp', mfa_code: generateTotpCode(secret), }) .expect(204) .execute(); const credentials = await createBuilder>(harness, target.token) .get('/users/@me/mfa/webauthn/credentials') .execute(); const credentialId = credentials[0]!.id; return { request: { path: `/admin/users/${target.userId}/webauthn-credentials/${credentialId}`, expectStatus: 204, }, expected: { action: 'delete_webauthn_credential', targetType: 'user', targetId: target.userId, metadata: {credential_id: credentialId}, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/mfa', async prepare(context) { const target = await createTestAccount(context.harness); await enableTotp(context, target); return { request: {path: `/admin/users/${target.userId}/mfa`, expectStatus: 204}, expected: { action: 'disable_mfa', targetType: 'user', targetId: target.userId, metadata: {}, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/profile-fields', async prepare({harness}) { const target = await createTestAccount(harness); return { request: { path: `/admin/users/${target.userId}/profile-fields`, body: {fields: ['bio', 'pronouns', 'global_name']}, }, expected: { action: 'clear_fields', targetType: 'user', targetId: target.userId, metadata: {fields: 'bio,pronouns,global_name'}, }, }; }, }, { method: 'PATCH', route: '/admin/users/:user_id/username', async prepare({harness}) { const target = await createTestAccount(harness); const username = createUniqueUsername('renamed'); return { request: {path: `/admin/users/${target.userId}/username`, body: {username}}, expected: { action: 'change_username', targetType: 'user', targetId: target.userId, metadata: { old_username: (await loadUser(target)).username, new_username: username, discriminator: expect.any(String), }, }, }; }, }, { method: 'PATCH', route: '/admin/users/:user_id/email', async prepare({harness}) { const target = await createTestAccount(harness); const email = createUniqueEmail('changed'); return { request: {path: `/admin/users/${target.userId}/email`, body: {email}}, expected: { action: 'change_email', targetType: 'user', targetId: target.userId, metadata: {old_email: (await loadUser(target)).email!, new_email: email}, }, }; }, }, { method: 'PUT', route: '/admin/users/:user_id/email-verification', async prepare({harness}) { const target = await createTestAccount(harness, {skipEmailVerification: true}); return { request: {path: `/admin/users/${target.userId}/email-verification`}, expected: { action: 'verify_email', targetType: 'user', targetId: target.userId, metadata: {email: (await loadUser(target)).email!}, }, }; }, }, { method: 'POST', route: '/admin/users/:user_id/verification-email', async prepare({harness}) { const target = await createTestAccount(harness, {skipEmailVerification: true}); return { request: {path: `/admin/users/${target.userId}/verification-email`, expectStatus: 204}, expected: { action: 'resend_verification_email', targetType: 'user', targetId: target.userId, metadata: {email: (await loadUser(target)).email!}, }, }; }, }, { method: 'POST', route: '/admin/users/:user_id/password-reset', async prepare({harness}) { const target = await createTestAccount(harness); return { request: {path: `/admin/users/${target.userId}/password-reset`, expectStatus: 204}, expected: { action: 'send_password_reset', targetType: 'user', targetId: target.userId, metadata: {email: (await loadUser(target)).email!}, }, }; }, }, { method: 'POST', route: '/admin/users/:user_id/password-reset-link', async prepare({harness}) { const target = await createTestAccount(harness); const originalSelfHosted = Config.instance.selfHosted; onTestFinished(() => { Config.instance.selfHosted = originalSelfHosted; }); Config.instance.selfHosted = true; await getInstanceConfigRepository().setAccountIdentityMode(AccountIdentityModes.USERNAME, 'setup'); return { request: {path: `/admin/users/${target.userId}/password-reset-link`}, expected: { action: 'create_password_reset_link', targetType: 'user', targetId: target.userId, metadata: {}, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/recovery-kit', async prepare({harness}) { const target = await createTestAccount(harness); const originalSelfHosted = Config.instance.selfHosted; onTestFinished(() => { Config.instance.selfHosted = originalSelfHosted; }); Config.instance.selfHosted = true; await getInstanceConfigRepository().setAccountIdentityMode(AccountIdentityModes.USERNAME, 'setup'); return { request: {path: `/admin/users/${target.userId}/recovery-kit`, expectStatus: 204}, expected: { action: 'revoke_recovery_kit', targetType: 'user', targetId: target.userId, metadata: {}, }, }; }, }, { method: 'PUT', route: '/admin/users/:user_id/ban', async prepare({harness}) { const target = await createTestAccount(harness); return { request: { path: `/admin/users/${target.userId}/ban`, body: {duration_hours: 24, reason: 'Coverage ban'}, }, expected: { action: 'temp_ban', targetType: 'user', targetId: target.userId, metadata: { duration_hours: '24', reason: 'Coverage ban', banned_until: expect.any(String), notify_user: 'true', notification_sent: 'true', }, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/ban', async prepare(context) { const target = await createTestAccount(context.harness); await adminBuilder(context).put(`/admin/users/${target.userId}/ban`).body({duration_hours: 24}).execute(); return { request: {path: `/admin/users/${target.userId}/ban`}, expected: { action: 'unban', targetType: 'user', targetId: target.userId, metadata: {notify_user: 'true', notification_sent: 'true', public_reason: 'null'}, }, }; }, }, { method: 'POST', route: '/admin/users/:user_id/ban/notes', auditLogReason: 'Coverage ban note', async prepare(context) { const target = await createTestAccount(context.harness); await adminBuilder(context) .put(`/admin/users/${target.userId}/ban`) .body({duration_hours: 24, reason: 'Coverage ban'}) .execute(); const banLog = (await getAdminRepository().listAllAuditLogsPaginated(1000)).find( (log) => log.action === 'temp_ban' && log.targetId.toString() === target.userId, ); return { request: { path: `/admin/users/${target.userId}/ban/notes`, body: {ban_audit_log_id: banLog!.logId.toString(), note: 'Coverage ban note'}, expectStatus: 204, }, expected: { action: 'annotate_ban', targetType: 'user', targetId: target.userId, metadata: {ban_audit_log_id: banLog!.logId.toString()}, }, }; }, }, { method: 'PUT', route: '/admin/users/:user_id/deletion', async prepare({harness}) { const target = await createTestAccount(harness); return { request: { path: `/admin/users/${target.userId}/deletion`, body: {reason_code: DeletionReasons.USER_REQUESTED, days_until_deletion: 30}, }, expected: { action: 'schedule_deletion', targetType: 'user', targetId: target.userId, metadata: { days: '30', reason_code: DeletionReasons.USER_REQUESTED.toString(), pending_deletion_at: expect.any(String), notify_user: 'true', notification_sent: 'true', notification_template: 'account_deletion_scheduled_requested', }, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/deletion', async prepare(context) { const target = await createTestAccount(context.harness); await adminBuilder(context) .put(`/admin/users/${target.userId}/deletion`) .body({reason_code: DeletionReasons.USER_REQUESTED, days_until_deletion: 30}) .execute(); const pendingDeletionAt = (await loadUser(target)).pendingDeletionAt!.toISOString(); return { request: { path: `/admin/users/${target.userId}/deletion`, body: {expected_pending_deletion_at: pendingDeletionAt}, }, expected: { action: 'cancel_deletion', targetType: 'user', targetId: target.userId, metadata: { cancelled_pending_deletion_at: pendingDeletionAt, cancelled_scheduled_by: context.admin.userId, cancelled_scheduled_at: expect.any(String), cancelled_reason_code: DeletionReasons.USER_REQUESTED.toString(), notify_user: 'false', notification_sent: 'false', }, }, }; }, }, { method: 'DELETE', route: '/admin/users/:user_id/message-deletion', async prepare({harness}) { const target = await createTestAccount(harness); await createBuilder(harness, target.token) .post('/users/@me/messages/delete') .body({password: target.password}) .expect(204) .execute(); return { request: {path: `/admin/users/${target.userId}/message-deletion`}, expected: { action: 'cancel_bulk_message_deletion', targetType: 'user', targetId: target.userId, metadata: {}, }, }; }, }, { method: 'PUT', route: '/admin/users/:user_id/acls', async prepare({harness}) { const target = await createTestAccount(harness); const acls = [AdminACLs.AUTHENTICATE, AdminACLs.USER_LOOKUP]; return { request: {path: `/admin/users/${target.userId}/acls`, body: {acls}}, expected: { action: 'set_acls', targetType: 'user', targetId: target.userId, metadata: {acls: acls.join(',')}, }, }; }, }, { method: 'PUT', route: '/admin/users/:user_id/traits', async prepare({harness}) { const target = await createTestAccount(harness); return { request: {path: `/admin/users/${target.userId}/traits`, body: {traits: ['coverage_trait']}}, expected: { action: 'set_traits', targetType: 'user', targetId: target.userId, metadata: {traits: 'coverage_trait'}, }, }; }, }, { method: 'PATCH', route: '/admin/users/:user_id/flags', async prepare({harness}) { const target = await createTestAccount(harness); const {flags} = await loadUser(target); return { request: { path: `/admin/users/${target.userId}/flags`, body: {add_flags: [UserFlags.PARTNER.toString()], remove_flags: [UserFlags.SPAMMER.toString()]}, }, expected: { action: 'update_flags', targetType: 'user', targetId: target.userId, metadata: { add_flags: UserFlags.PARTNER.toString(), remove_flags: UserFlags.SPAMMER.toString(), new_flags: ((flags | UserFlags.PARTNER) & ~UserFlags.SPAMMER).toString(), }, }, }; }, }, { method: 'PATCH', route: '/admin/users/:user_id/premium-flags', async prepare({harness}) { const target = await createTestAccount(harness); const {premiumFlags} = await loadUser(target); return { request: { path: `/admin/users/${target.userId}/premium-flags`, body: {add_flags: [PremiumFlags.BADGE_HIDDEN], remove_flags: [PremiumFlags.PURCHASE_DISABLED]}, }, expected: { action: 'update_premium_flags', targetType: 'user', targetId: target.userId, metadata: { add_flags: PremiumFlags.BADGE_HIDDEN.toString(), remove_flags: PremiumFlags.PURCHASE_DISABLED.toString(), new_flags: ((premiumFlags | PremiumFlags.BADGE_HIDDEN) & ~PremiumFlags.PURCHASE_DISABLED).toString(), }, }, }; }, }, { method: 'PATCH', route: '/admin/users/:user_id/date-of-birth', async prepare({harness}) { const target = await createTestAccount(harness); return { request: {path: `/admin/users/${target.userId}/date-of-birth`, body: {date_of_birth: '1995-06-15'}}, expected: { action: 'change_dob', targetType: 'user', targetId: target.userId, metadata: {old_dob: (await loadUser(target)).dateOfBirth!, new_dob: '1995-06-15'}, }, }; }, }, ];