This repository has no description
Something went wrong. Try again.
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586// SPDX-License-Identifier: AGPL-3.0-or-later
import {afterEach, beforeEach, describe, expect, test} from 'vitest';import {createTestAccount, setUserACLs} from '../../auth/tests/AuthTestUtils';import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness';import {HTTP_STATUS} from '../../test/TestConstants';import {createBuilder} from '../../test/TestRequestBuilder';import { createAdminApiKey, createAdminApiKeyWithDefaultACLs, listAdminApiKeys, revokeAdminApiKey,} from './AdminTestUtils';
describe('Admin API Key Management', () => { let harness: ApiTestHarness; beforeEach(async () => { harness = await createApiTestHarness(); }); afterEach(async () => { await harness?.shutdown(); }); describe('API Key ACL Validation', () => { test('user can only grant ACLs they possess', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'audit_log:view']); await createBuilder(harness, `${admin.token}`) .post('/admin/api-keys') .body({ name: 'Test Key', acls: ['audit_log:view'], }) .expect(HTTP_STATUS.OK) .execute(); }); test('user cannot grant ACLs they do not possess', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'audit_log:view']); await createBuilder(harness, `${admin.token}`) .post('/admin/api-keys') .body({ name: 'Test Key', acls: ['audit_log:view', 'user:lookup'], }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('user with wildcard ACL can grant any ACL', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['*']); await createBuilder(harness, `${admin.token}`) .post('/admin/api-keys') .body({ name: 'Wildcard Test Key', acls: ['audit_log:view', 'user:lookup', 'guild:lookup', 'archive:trigger:user'], }) .expect(HTTP_STATUS.OK) .execute(); }); test('must have admin_api_key:manage ACL to create keys', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'audit_log:view']); await createBuilder(harness, `${admin.token}`) .post('/admin/api-keys') .body({ name: 'Test Key', acls: ['audit_log:view'], }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('ACLs are stored and retrievable correctly', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const requestedACLs = ['audit_log:view', 'user:lookup', 'guild:lookup']; await createAdminApiKey(harness, admin, 'ACL Storage Test', requestedACLs, null); const keys = await listAdminApiKeys(harness, admin.token); expect(keys).toHaveLength(1); const keyACLs = keys[0]!.acls as Array<string>; expect(keyACLs).toHaveLength(requestedACLs.length); expect(keyACLs).toEqual(expect.arrayContaining(requestedACLs)); }); test('empty ACL list is valid', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'audit_log:view']); await createBuilder(harness, `${admin.token}`) .post('/admin/api-keys') .body({ name: 'Test Key', acls: [], }) .expect(HTTP_STATUS.OK) .execute(); }); }); describe('API Key Authentication', () => { test('valid API key authenticates successfully', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin, 'Auth Test Key'); await createBuilder(harness, apiKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.OK) .execute(); }); test('invalid API key is rejected', async () => { await createTestAccount(harness); await createBuilder(harness, 'Admin invalid_key_12345') .post('/admin/users/lookup') .body({ user_ids: ['123456789'], }) .expect(HTTP_STATUS.UNAUTHORIZED) .execute(); }); test('API key requires Admin prefix', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin, 'Prefix Test Key'); await createBuilder(harness, `Bearer ${apiKey.key}`) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.UNAUTHORIZED) .execute(); }); test('Admin prefix is case sensitive', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin, 'Case Test Key'); await createBuilder(harness, `admin ${apiKey.key}`) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.UNAUTHORIZED) .execute(); }); test('API key cannot authenticate to user endpoints', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin, 'User Endpoint Test Key'); await createBuilder(harness, apiKey.token).get('/users/@me').expect(HTTP_STATUS.UNAUTHORIZED).execute(); }); test('updates last_used_at timestamp on use', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin, 'Last Used Test Key'); const keysBefore = await listAdminApiKeys(harness, admin.token); expect(keysBefore).toHaveLength(1); expect(keysBefore[0]!.last_used_at).toBeNull(); await createBuilder(harness, apiKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .execute(); const keysAfter = await listAdminApiKeys(harness, admin.token); expect(keysAfter).toHaveLength(1); expect(keysAfter[0]!.last_used_at).not.toBeNull(); }); }); describe('API Key Authorization (ACL Restrictions)', () => { test('key can access endpoints with granted ACLs', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', ]); const apiKey = await createAdminApiKey(harness, admin, 'Test Key', ['audit_log:view', 'user:lookup'], null); await createBuilder(harness, apiKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.OK) .execute(); }); test('key cannot access endpoints without required ACLs', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', ]); const apiKey = await createAdminApiKey(harness, admin, 'Limited Key', ['audit_log:view'], null); await createBuilder(harness, apiKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('key with wildcard ACL can access all endpoints', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['*']); const apiKey = await createAdminApiKey(harness, admin, 'Wildcard Key', ['*'], null); await createBuilder(harness, apiKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.OK) .execute(); }); test('multiple keys with different ACLs work independently', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const auditKey = await createAdminApiKey(harness, admin, 'Audit Log Key', ['audit_log:view'], null); const userKey = await createAdminApiKey(harness, admin, 'Users Key', ['user:lookup'], null); await createBuilder(harness, userKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.OK) .execute(); await createBuilder(harness, auditKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('list API keys requires admin_api_key:manage ACL', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage']); const apiKeyWithACL = await createAdminApiKey(harness, admin, 'List Test', ['admin_api_key:manage'], null); await createBuilder(harness, apiKeyWithACL.token).get('/admin/api-keys').expect(HTTP_STATUS.OK).execute(); const apiKeyWithoutACL = await createAdminApiKey(harness, admin, 'List Test No ACL', [], null); await createBuilder(harness, apiKeyWithoutACL.token) .get('/admin/api-keys') .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('delete API key requires admin_api_key:manage ACL', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage']); const keyToDelete = await createAdminApiKey(harness, admin, 'To Delete', [], null); const deleterKey = await createAdminApiKey(harness, admin, 'Deleter', ['admin_api_key:manage'], null); await createBuilder(harness, deleterKey.token) .delete(`/admin/api-keys/${keyToDelete.keyId}`) .body(null) .expect(HTTP_STATUS.OK) .execute(); }); test('delete API key fails without admin_api_key:manage ACL', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage']); const keyToDelete = await createAdminApiKey(harness, admin, 'To Delete 2', [], null); const deleterKey = await createAdminApiKey(harness, admin, 'Deleter No ACL', [], null); await createBuilder(harness, deleterKey.token) .delete(`/admin/api-keys/${keyToDelete.keyId}`) .body(null) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); }); describe('API Key Revocation', () => { test('basic revocation removes key from list', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin, 'Revoke Test'); let keys = await listAdminApiKeys(harness, admin.token); expect(keys).toHaveLength(1); expect(keys.some((k) => k.key_id === apiKey.keyId)).toBe(true); await revokeAdminApiKey(harness, admin.token, apiKey.keyId); keys = await listAdminApiKeys(harness, admin.token); expect(keys).toHaveLength(0); }); test('revoked key cannot be used for authentication', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin, 'Revoke Auth Test'); await createBuilder(harness, apiKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.OK) .execute(); await revokeAdminApiKey(harness, admin.token, apiKey.keyId); await createBuilder(harness, apiKey.token) .post('/admin/users/lookup') .body({ user_ids: [admin.userId], }) .expect(HTTP_STATUS.UNAUTHORIZED) .execute(); }); test('revocation of non-existent key returns 404', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); await createBuilder(harness, `${admin.token}`) .delete('/admin/api-keys/nonexistent-id') .body(null) .expect(HTTP_STATUS.NOT_FOUND) .execute(); }); test('revocation requires admin_api_key:manage ACL', async () => { const admin1 = await createTestAccount(harness); const admin2 = await createTestAccount(harness); await setUserACLs(harness, admin1, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); await setUserACLs(harness, admin2, ['admin:authenticate']); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin1, 'Admin1 Key'); await createBuilder(harness, `${admin2.token}`) .delete(`/admin/api-keys/${apiKey.keyId}`) .body(null) .expect(HTTP_STATUS.FORBIDDEN) .execute(); const keys = await listAdminApiKeys(harness, admin1.token); expect(keys.some((k) => k.key_id === apiKey.keyId)).toBe(true); }); test('cannot revoke other users keys', async () => { const admin1 = await createTestAccount(harness); const admin2 = await createTestAccount(harness); await setUserACLs(harness, admin1, [ 'admin:authenticate', 'admin_api_key:manage', 'audit_log:view', 'user:lookup', 'guild:lookup', ]); await setUserACLs(harness, admin2, ['admin:authenticate', 'admin_api_key:manage', 'audit_log:view']); const apiKey = await createAdminApiKeyWithDefaultACLs(harness, admin1, 'Admin1 Key'); await createBuilder(harness, `${admin2.token}`) .delete(`/admin/api-keys/${apiKey.keyId}`) .body(null) .expect(HTTP_STATUS.NOT_FOUND) .execute(); const keys = await listAdminApiKeys(harness, admin1.token); expect(keys.some((k) => k.key_id === apiKey.keyId)).toBe(true); }); }); describe('Setting User ACLs Requires Proper ACL', () => { test('setting user ACLs requires acl:set:user ACL', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'acl:set:user']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/set-acls') .body({ user_id: targetUser.userId, acls: ['admin:authenticate'], }) .expect(HTTP_STATUS.OK) .execute(); }); test('setting user ACLs fails without acl:set:user ACL', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/set-acls') .body({ user_id: targetUser.userId, acls: ['admin:authenticate'], }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('API key can set user ACLs with acl:set:user', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'acl:set:user']); const apiKey = await createAdminApiKey( harness, admin, 'ACL Setter Key', ['admin:authenticate', 'acl:set:user'], null, ); await createBuilder(harness, apiKey.token) .post('/admin/users/set-acls') .body({ user_id: targetUser.userId, acls: ['admin:authenticate'], }) .expect(HTTP_STATUS.OK) .execute(); }); test('API key cannot set user ACLs without acl:set:user', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'user:lookup']); const apiKey = await createAdminApiKey(harness, admin, 'No ACL Setter Key', ['user:lookup'], null); await createBuilder(harness, apiKey.token) .post('/admin/users/set-acls') .body({ user_id: targetUser.userId, acls: ['admin:authenticate'], }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('setting ACLs on non-existent user fails', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'acl:set:user']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/set-acls') .body({ user_id: '999999999999999999', acls: ['admin:authenticate'], }) .expect(HTTP_STATUS.NOT_FOUND) .execute(); }); }); describe('Deletion Schedule Minimum Validation', () => { test('schedule deletion requires user:delete ACL', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:delete']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/schedule-deletion') .body({ user_id: targetUser.userId, reason_code: 1, days_until_deletion: 60, }) .expect(HTTP_STATUS.OK) .execute(); }); test('schedule deletion fails without user:delete ACL', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/schedule-deletion') .body({ user_id: targetUser.userId, reason_code: 1, days_until_deletion: 60, }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('deletion schedule enforces minimum days for user requested deletion', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:delete']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/schedule-deletion') .body({ user_id: targetUser.userId, reason_code: 0, days_until_deletion: 1, }) .expect(HTTP_STATUS.OK) .executeWithResponse(); }); test('deletion schedule enforces minimum days for standard deletion', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:delete']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/schedule-deletion') .body({ user_id: targetUser.userId, reason_code: 1, days_until_deletion: 1, }) .expect(HTTP_STATUS.OK) .executeWithResponse(); }); test('API key can schedule deletion with user:delete ACL', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'user:delete']); const apiKey = await createAdminApiKey(harness, admin, 'Deletion Key', ['user:delete'], null); await createBuilder(harness, apiKey.token) .post('/admin/users/schedule-deletion') .body({ user_id: targetUser.userId, reason_code: 1, days_until_deletion: 60, }) .expect(HTTP_STATUS.OK) .execute(); }); test('API key cannot schedule deletion without user:delete ACL', async () => { const admin = await createTestAccount(harness); const targetUser = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'user:lookup']); const apiKey = await createAdminApiKey(harness, admin, 'No Deletion Key', ['user:lookup'], null); await createBuilder(harness, apiKey.token) .post('/admin/users/schedule-deletion') .body({ user_id: targetUser.userId, reason_code: 1, days_until_deletion: 60, }) .expect(HTTP_STATUS.FORBIDDEN) .execute(); }); test('schedule deletion on non-existent user fails', async () => { const admin = await createTestAccount(harness); await setUserACLs(harness, admin, ['admin:authenticate', 'user:delete']); await createBuilder(harness, `${admin.token}`) .post('/admin/users/schedule-deletion') .body({ user_id: '999999999999999999', reason_code: 1, days_until_deletion: 60, }) .expect(HTTP_STATUS.NOT_FOUND) .execute(); }); });});