mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 20:54:07 -08:00
fix(core): Block Public API related REST calls when Public API is not enabled (#9521)
This commit is contained in:
parent
e07de837b9
commit
ac4e0fbb47
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 });
|
||||||
|
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
Loading…
Reference in a new issue