feat(admin)!: move the admin api to rest and fix its defects (#2515)

This commit is contained in:
Hampus authored and GitHub committed 2026-09-06 15:36:41 +02:00
1 parent a70924d4b0
commit 7f8f09ee51
143 files changed
+15238 -12790

No files matched your search

@@ -1,6 +1,8 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
import {ADMIN_OAUTH2_APPLICATION_ID} from '@fluxer/constants/src/Core';
import {ForbiddenError} from '@fluxer/errors/src/domains/core/ForbiddenError';
import {type ApplicationID, createApplicationID, type UserID} from '../../BrandedTypes';
import {Config} from '../../Config';
import {SYSTEM_USER_ID} from '../../constants/Core';
@@ -93,7 +95,10 @@ export class ApplicationRepository implements IApplicationRepository {
async upsertApplication(data: ApplicationRow, oldData?: ApplicationRow | null): Promise<Application> {
const applicationId = data.application_id;
if (applicationId === createApplicationID(ADMIN_OAUTH2_APPLICATION_ID)) {
throw new Error('Cannot modify the built-in admin OAuth2 application');
throw new ForbiddenError({
code: APIErrorCodes.FORBIDDEN,
message: 'Cannot modify the built-in admin OAuth2 application',
});
}
const result = await executeVersionedUpdate<ApplicationRow, 'application_id'>(
async () => fetchOne<ApplicationRow>(SELECT_APPLICATION_CQL, {application_id: applicationId}),
@@ -125,7 +130,10 @@ export class ApplicationRepository implements IApplicationRepository {
async deleteApplication(applicationId: ApplicationID): Promise<void> {
if (applicationId === createApplicationID(ADMIN_OAUTH2_APPLICATION_ID)) {
throw new Error('Cannot delete the built-in admin OAuth2 application');
throw new ForbiddenError({
code: APIErrorCodes.FORBIDDEN,
message: 'Cannot delete the built-in admin OAuth2 application',
});
}
const applicationRow = await fetchOne<ApplicationRow>(SELECT_APPLICATION_CQL, {application_id: applicationId});
const application = applicationRow ? new Application(applicationRow) : null;
@@ -411,10 +411,7 @@ describe('OAuth2 Scope Enforcement', () => {
ADMIN_OAUTH2_APPLICATION_ID.toString(),
);
await createBuilder(harness, `Bearer ${oauth2Token.token}`)
.post('/admin/users/lookup')
.body({
user_ids: [admin.userId],
})
.get(`/admin/users/${admin.userId}`)
.expect(HTTP_STATUS.OK)
.execute();
});
@@ -423,10 +420,7 @@ describe('OAuth2 Scope Enforcement', () => {
await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']);
const oauth2Token = await createOAuth2Token(harness, admin.userId, ['identify', 'email']);
await createBuilder(harness, `Bearer ${oauth2Token.token}`)
.post('/admin/users/lookup')
.body({
user_ids: [admin.userId],
})
.get(`/admin/users/${admin.userId}`)
.expect(HTTP_STATUS.FORBIDDEN, 'ACCESS_DENIED')
.execute();
});
@@ -434,10 +428,7 @@ describe('OAuth2 Scope Enforcement', () => {
const admin = await createTestAccount(harness);
await setUserACLs(harness, admin, ['admin:authenticate', 'user:lookup']);
await createBuilder(harness, `${admin.token}`)
.post('/admin/users/lookup')
.body({
user_ids: [admin.userId],
})
.get(`/admin/users/${admin.userId}`)
.expect(HTTP_STATUS.OK)
.execute();
});
@@ -445,13 +436,7 @@ describe('OAuth2 Scope Enforcement', () => {
const admin = await createTestAccount(harness);
await setUserACLs(harness, admin, ['admin:authenticate', 'admin_api_key:manage', 'user:lookup']);
const apiKey = await createAdminApiKey(harness, admin, 'Test Key', ['user:lookup'], null);
await createBuilder(harness, apiKey.token)
.post('/admin/users/lookup')
.body({
user_ids: [admin.userId],
})
.expect(HTTP_STATUS.OK)
.execute();
await createBuilder(harness, apiKey.token).get(`/admin/users/${admin.userId}`).expect(HTTP_STATUS.OK).execute();
});
test('built-in admin OAuth2 token still requires proper user ACLs', async () => {
const admin = await createTestAccount(harness);
@@ -463,10 +448,7 @@ describe('OAuth2 Scope Enforcement', () => {
ADMIN_OAUTH2_APPLICATION_ID.toString(),
);
await createBuilder(harness, `Bearer ${oauth2Token.token}`)
.post('/admin/users/lookup')
.body({
user_ids: [admin.userId],
})
.get(`/admin/users/${admin.userId}`)
.expect(HTTP_STATUS.FORBIDDEN, 'MISSING_ACL')
.execute();
});
@@ -479,10 +461,7 @@ describe('OAuth2 Scope Enforcement', () => {
ADMIN_OAUTH2_APPLICATION_ID.toString(),
);
await createBuilder(harness, `Bearer ${oauth2Token.token}`)
.post('/admin/users/lookup')
.body({
user_ids: [user.userId],
})
.get(`/admin/users/${user.userId}`)
.expect(HTTP_STATUS.FORBIDDEN, 'MISSING_PERMISSIONS')
.execute();
});
@@ -509,8 +488,7 @@ describe('OAuth2 Scope Enforcement', () => {
code: string;
message: string;
}>(harness, `Bearer ${oauth2Token.token}`)
.post('/admin/users/lookup')
.body({user_ids: [admin.userId]})
.get(`/admin/users/${admin.userId}`)
.expect(HTTP_STATUS.FORBIDDEN, 'ACCESS_DENIED')
.execute();
expect(json.code).toBe('ACCESS_DENIED');
@@ -528,8 +506,7 @@ describe('OAuth2 Scope Enforcement', () => {
code: string;
message: string;
}>(harness, `Bearer ${oauth2Token.token}`)
.post('/admin/users/lookup')
.body({user_ids: [admin.userId]})
.get(`/admin/users/${admin.userId}`)
.expect(HTTP_STATUS.FORBIDDEN, 'MISSING_ACL')
.execute();
expect(json.code).toBe('MISSING_ACL');