fix(core): Block Public API related REST calls when Public API is not enabled (#9521)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2024-05-28 14:43:22 +02:00 committed by GitHub
parent e07de837b9
commit ac4e0fbb47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 15 deletions

View file

@ -153,11 +153,6 @@ export const loadPublicApiVersions = async (
}; };
}; };
function isApiEnabledByLicense(): boolean {
const license = Container.get(License);
return !license.isAPIDisabled();
}
export function isApiEnabled(): boolean { export function isApiEnabled(): boolean {
return !config.get('publicApi.disabled') && isApiEnabledByLicense(); return !config.get('publicApi.disabled') && !Container.get(License).isAPIDisabled();
} }

View file

@ -1,6 +1,6 @@
import validator from 'validator'; import validator from 'validator';
import { plainToInstance } from 'class-transformer'; import { plainToInstance } from 'class-transformer';
import { Response } from 'express'; import { type RequestHandler, Response } from 'express';
import { randomBytes } from 'crypto'; import { randomBytes } from 'crypto';
import { AuthService } from '@/auth/auth.service'; import { AuthService } from '@/auth/auth.service';
@ -22,6 +22,15 @@ import { ExternalHooks } from '@/ExternalHooks';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { BadRequestError } from '@/errors/response-errors/bad-request.error';
import { UserRepository } from '@/databases/repositories/user.repository'; import { UserRepository } from '@/databases/repositories/user.repository';
import { isApiEnabled } from '@/PublicApi';
export const isApiEnabledMiddleware: RequestHandler = (_, res, next) => {
if (isApiEnabled()) {
next();
} else {
res.status(404).end();
}
};
@RestController('/me') @RestController('/me')
export class MeController { export class MeController {
@ -185,7 +194,7 @@ export class MeController {
/** /**
* Creates an API Key * Creates an API Key
*/ */
@Post('/api-key') @Post('/api-key', { middlewares: [isApiEnabledMiddleware] })
async createAPIKey(req: AuthenticatedRequest) { async createAPIKey(req: AuthenticatedRequest) {
const apiKey = `n8n_api_${randomBytes(40).toString('hex')}`; const apiKey = `n8n_api_${randomBytes(40).toString('hex')}`;
@ -202,7 +211,7 @@ export class MeController {
/** /**
* Get an API Key * Get an API Key
*/ */
@Get('/api-key') @Get('/api-key', { middlewares: [isApiEnabledMiddleware] })
async getAPIKey(req: AuthenticatedRequest) { async getAPIKey(req: AuthenticatedRequest) {
return { apiKey: req.user.apiKey }; return { apiKey: req.user.apiKey };
} }
@ -210,7 +219,7 @@ export class MeController {
/** /**
* Deletes an API Key * Deletes an API Key
*/ */
@Delete('/api-key') @Delete('/api-key', { middlewares: [isApiEnabledMiddleware] })
async deleteAPIKey(req: AuthenticatedRequest) { async deleteAPIKey(req: AuthenticatedRequest) {
await this.userService.update(req.user.id, { apiKey: null }); await this.userService.update(req.user.id, { apiKey: null });

View file

@ -31,6 +31,7 @@ import type { CommunityPackagesService } from '@/services/communityPackages.serv
import { Logger } from '@/Logger'; import { Logger } from '@/Logger';
import { UrlService } from './url.service'; import { UrlService } from './url.service';
import { InternalHooks } from '@/InternalHooks'; import { InternalHooks } from '@/InternalHooks';
import { isApiEnabled } from '@/PublicApi';
@Service() @Service()
export class FrontendService { export class FrontendService {
@ -143,7 +144,7 @@ export class FrontendService {
}, },
}, },
publicApi: { publicApi: {
enabled: !config.get('publicApi.disabled') && !this.license.isAPIDisabled(), enabled: isApiEnabled(),
latestVersion: 1, latestVersion: 1,
path: config.getEnv('publicApi.path'), path: config.getEnv('publicApi.path'),
swaggerUi: { swaggerUi: {

View file

@ -1,7 +1,12 @@
import { Container } from 'typedi';
import type { SuperAgentTest } from 'supertest'; import type { SuperAgentTest } from 'supertest';
import { IsNull } from '@n8n/typeorm'; import { IsNull } from '@n8n/typeorm';
import validator from 'validator'; import validator from 'validator';
import type { User } from '@db/entities/User'; import type { User } from '@db/entities/User';
import { UserRepository } from '@db/repositories/user.repository';
import { ProjectRepository } from '@db/repositories/project.repository';
import { SUCCESS_RESPONSE_BODY } from './shared/constants'; import { SUCCESS_RESPONSE_BODY } from './shared/constants';
import { import {
randomApiKey, randomApiKey,
@ -12,15 +17,38 @@ import {
} from './shared/random'; } from './shared/random';
import * as testDb from './shared/testDb'; import * as testDb from './shared/testDb';
import * as utils from './shared/utils/'; import * as utils from './shared/utils/';
import { addApiKey, createUser, createUserShell } from './shared/db/users'; import { addApiKey, createOwner, createUser, createUserShell } from './shared/db/users';
import Container from 'typedi'; import config from '@/config';
import { UserRepository } from '@db/repositories/user.repository';
import { ProjectRepository } from '@/databases/repositories/project.repository';
const testServer = utils.setupTestServer({ endpointGroups: ['me'] }); const testServer = utils.setupTestServer({ endpointGroups: ['me'] });
beforeEach(async () => { beforeEach(async () => {
await testDb.truncate(['User']); await testDb.truncate(['User']);
config.set('publicApi.disabled', false);
});
describe('When public API is disabled', () => {
let owner: User;
let authAgent: SuperAgentTest;
beforeEach(async () => {
owner = await createOwner();
await addApiKey(owner);
authAgent = testServer.authAgentFor(owner);
config.set('publicApi.disabled', true);
});
test('POST /me/api-key should 404', async () => {
await authAgent.post('/me/api-key').expect(404);
});
test('GET /me/api-key should 404', async () => {
await authAgent.get('/me/api-key').expect(404);
});
test('DELETE /me/api-key should 404', async () => {
await authAgent.delete('/me/api-key').expect(404);
});
}); });
describe('Owner shell', () => { describe('Owner shell', () => {