diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts index dad1a4132..45fb47376 100644 --- a/packages/backend/src/server/api/ApiCallService.ts +++ b/packages/backend/src/server/api/ApiCallService.ts @@ -83,7 +83,7 @@ export class ApiCallService implements OnApplicationShutdown { } }).catch(err => { if (err instanceof AuthenticationError) { - this.send(reply, 403, new ApiError({ + this.send(reply, 401, new ApiError({ message: 'Authentication failed. Please ensure your token is correct.', code: 'AUTHENTICATION_FAILED', id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14', @@ -137,7 +137,7 @@ export class ApiCallService implements OnApplicationShutdown { } }).catch(err => { if (err instanceof AuthenticationError) { - this.send(reply, 403, new ApiError({ + this.send(reply, 401, new ApiError({ message: 'Authentication failed. Please ensure your token is correct.', code: 'AUTHENTICATION_FAILED', id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14', @@ -278,6 +278,7 @@ export class ApiCallService implements OnApplicationShutdown { throw new ApiError({ message: 'You are not assigned to a moderator role.', code: 'ROLE_PERMISSION_DENIED', + kind: 'permission', id: 'd33d5333-db36-423d-a8f9-1a2b9549da41', }); } @@ -285,6 +286,7 @@ export class ApiCallService implements OnApplicationShutdown { throw new ApiError({ message: 'You are not assigned to an administrator role.', code: 'ROLE_PERMISSION_DENIED', + kind: 'permission', id: 'c3d38592-54c0-429d-be96-5636b0431a61', }); } @@ -296,6 +298,7 @@ export class ApiCallService implements OnApplicationShutdown { throw new ApiError({ message: 'You are not assigned to a required role.', code: 'ROLE_PERMISSION_DENIED', + kind: 'permission', id: '7f86f06f-7e15-4057-8561-f4b6d4ac755a', }); } @@ -305,6 +308,7 @@ export class ApiCallService implements OnApplicationShutdown { throw new ApiError({ message: 'Your app does not have the necessary permissions to use this endpoint.', code: 'PERMISSION_DENIED', + kind: 'permission', id: '1370e5b7-d4eb-4566-bb1d-7748ee6a1838', }); } diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts index 194ded7e8..4b9167b6b 100644 --- a/packages/backend/test/e2e/api.ts +++ b/packages/backend/test/e2e/api.ts @@ -1,7 +1,7 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { signup, api, startServer } from '../utils.js'; +import { signup, api, startServer, successfulApiCall, failedApiCall } from '../utils.js'; import type { INestApplicationContext } from '@nestjs/common'; import type * as misskey from 'misskey-js'; @@ -81,4 +81,46 @@ describe('API', () => { assert.strictEqual(res.body.nullableDefault, 'hello'); }); }); + + test('管理者専用のAPIのアクセス制限', async () => { + // aliceは管理者、APIを使える + await successfulApiCall({ + endpoint: '/admin/get-index-stats', + parameters: {}, + user: alice, + }); + + // bobは一般ユーザーだからダメ + await failedApiCall({ + endpoint: '/admin/get-index-stats', + parameters: {}, + user: bob, + }, { + status: 403, + code: 'ROLE_PERMISSION_DENIED', + id: 'c3d38592-54c0-429d-be96-5636b0431a61', + }); + + // publicアクセスももちろんダメ + await failedApiCall({ + endpoint: '/admin/get-index-stats', + parameters: {}, + user: undefined, + }, { + status: 401, + code: 'CREDENTIAL_REQUIRED', + id: '1384574d-a912-4b81-8601-c7b1c4085df1', + }); + + // ごまがしもダメ + await failedApiCall({ + endpoint: '/admin/get-index-stats', + parameters: {}, + user: { token: 'tsukawasete' }, + }, { + status: 401, + code: 'AUTHENTICATION_FAILED', + id: 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14', + }); + }); });