fix(admin): omit synthetic accounts from user lookup and search (#2705)

This commit is contained in:
Hampus
2026-09-11 22:06:29 +02:00
committed by GitHub
parent f254ed679b
commit b30a4f5d14
4 changed files with 44 additions and 6 deletions
@@ -5,6 +5,7 @@ import {InputValidationError} from '@fluxer/errors/src/domains/core/InputValidat
import type {WorkerJobPayload} from '@pkgs/worker/src/contracts/WorkerTypes';
import type {ApiContext} from '../../ApiContext';
import {createGuildID, createUserID, type UserID} from '../../BrandedTypes';
import {isSyntheticUserId} from '../../constants/Core';
import type {IGuildRepositoryAggregate} from '../../guild/repositories/IGuildRepositoryAggregate';
import {Logger} from '../../Logger';
import {getGuildSearchService, getUserSearchService} from '../../SearchFactory';
@@ -129,12 +130,11 @@ export class AdminSearchService {
throw new FeatureTemporarilyDisabledError();
}
const query = data.query?.trim() || '';
const isIdQuery = /^\d+$/.test(query);
const directUserId = /^\d+$/.test(query) ? createUserID(BigInt(query)) : null;
const canResolveDirectUser = directUserId !== null && !isSyntheticUserId(directUserId) && data.offset === 0;
const [searchResult, directUser] = await Promise.all([
userSearchService.search(query, {}, {limit: data.limit, offset: data.offset}),
isIdQuery && data.offset === 0
? userRepository.findUnique(createUserID(BigInt(query))).catch(() => null)
: Promise.resolve(null),
canResolveDirectUser ? userRepository.findUnique(directUserId).catch(() => null) : Promise.resolve(null),
]);
const {hits, total} = searchResult;
const userIds = hits.map((hit) => createUserID(BigInt(hit.id)));
@@ -3,6 +3,7 @@
import type {LookupUserRequest} from '@fluxer/schema/src/domains/admin/AdminUserSchemas';
import type {ApiContext} from '../../ApiContext';
import {createUserID} from '../../BrandedTypes';
import {isSyntheticUserId} from '../../constants/Core';
import {Logger} from '../../Logger';
import {mapUserToAdminResponse} from '../models/UserTypes';
@@ -32,7 +33,7 @@ export class AdminUserLookupService {
} else if (/^\d+$/.test(query)) {
try {
const userId = createUserID(BigInt(query));
user = await userRepository.findUnique(userId);
user = isSyntheticUserId(userId) ? null : await userRepository.findUnique(userId);
} catch (error) {
Logger.debug({query, error}, 'Failed to lookup user by numeric ID, invalid ID format');
user = null;
@@ -21,6 +21,8 @@ interface UserListResponse {
total: number;
}
const SYNTHETIC_USER_IDS = ['0', '1'];
async function setLastActiveIp(harness: ApiTestHarness, token: string, ip: string): Promise<void> {
await createBuilder(harness, `${token}`)
.get('/users/@me')
@@ -170,5 +172,35 @@ describe('Admin user directory', () => {
expect(result.users.map((user) => user.id)).toEqual([target.userId]);
expect(result.users[0]?.email).toBeNull();
});
test.each(SYNTHETIC_USER_IDS)('omits the synthetic account %s from the resolve selector', async (userId) => {
const admin = await createTestAccount(harness);
await setUserACLs(harness, admin, [AdminACLs.AUTHENTICATE, AdminACLs.USER_LOOKUP]);
const result = await createBuilder<UserListResponse>(harness, `${admin.token}`)
.get(`/admin/users?resolve=${userId}`)
.expect(HTTP_STATUS.OK)
.execute();
expect(result.users).toEqual([]);
expect(result.total).toBe(0);
});
test.each(SYNTHETIC_USER_IDS)('omits the synthetic account %s from the q selector', async (userId) => {
const admin = await createTestAccount(harness);
await setUserACLs(harness, admin, [AdminACLs.AUTHENTICATE, AdminACLs.USER_LOOKUP]);
const result = await createBuilder<UserListResponse>(harness, `${admin.token}`)
.get(`/admin/users?q=${userId}`)
.expect(HTTP_STATUS.OK)
.execute();
expect(result.users.map((user) => user.id)).not.toContain(userId);
});
});
describe('GET /admin/users/:user_id', () => {
test.each(SYNTHETIC_USER_IDS)('reports no user for the synthetic account %s', async (userId) => {
const admin = await createTestAccount(harness);
await setUserACLs(harness, admin, [AdminACLs.AUTHENTICATE, AdminACLs.USER_LOOKUP]);
const result = await createBuilder<UserListResponse>(harness, `${admin.token}`)
.get(`/admin/users/${userId}`)
.expect(HTTP_STATUS.OK)
.execute();
expect(result.users).toEqual([]);
});
});
});
+6 -1
View File
@@ -1,5 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {createUserID} from '../BrandedTypes';
import {DELETED_USER_ID} from '@fluxer/constants/src/UserConstants';
import {createUserID, type UserID} from '../BrandedTypes';
export const SYSTEM_USER_ID = createUserID(0n);
export function isSyntheticUserId(userId: UserID): boolean {
return userId === SYSTEM_USER_ID || userId === DELETED_USER_ID;
}