From ff075c82fa6b3c4ef07b4faf28ae68f4c8bf627d Mon Sep 17 00:00:00 2001 From: thiagobustamante Date: Thu, 14 Dec 2017 11:21:54 -0200 Subject: [PATCH] validate API ID --- src/admin/api/api.ts | 6 ++++-- src/admin/config/cli-args.ts | 2 +- src/config/api.ts | 11 +++++++++-- src/config/gateway.ts | 6 ++++++ src/gateway.ts | 2 +- test/unit/install-apis.spec.ts | 23 +++++++++++++++++++++++ 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/admin/api/api.ts b/src/admin/api/api.ts index 58ebb5b5..d30875e7 100644 --- a/src/admin/api/api.ts +++ b/src/admin/api/api.ts @@ -5,12 +5,14 @@ import { ApiConfig, validateApiConfig } from '../../config/api'; import { ApiService } from '../../service/api'; import { Inject } from 'typescript-ioc'; import * as swagger from 'typescript-rest-swagger'; +import { Configuration } from '../../configuration'; @Path('apis') @swagger.Tags('APIs') @swagger.Security('Bearer') export class APIRest { @Inject private service: ApiService; + @Inject private config: Configuration; @GET list( @QueryParam('name') name?: string, @@ -23,7 +25,7 @@ export class APIRest { @POST addApi(api: ApiConfig): Promise> { return new Promise>((resolve, reject) => { - validateApiConfig(api) + validateApiConfig(api, this.config.gateway.disableApiIdValidation) .then(() => this.service.create(api)) .then((apiId) => resolve(new Return.NewResource(`apis/${apiId}`))) .catch(reject); @@ -36,7 +38,7 @@ export class APIRest { return new Promise((resolve, reject) => { api.id = id; - validateApiConfig(api) + validateApiConfig(api, this.config.gateway.disableApiIdValidation) .then(() => this.service.update(api)) .then(() => resolve()) .catch(reject); diff --git a/src/admin/config/cli-args.ts b/src/admin/config/cli-args.ts index 0013dfcd..f1a1e028 100644 --- a/src/admin/config/cli-args.ts +++ b/src/admin/config/cli-args.ts @@ -13,7 +13,7 @@ const parser = new ArgumentParser({ parser.addArgument( ['-c', '--config'], { - help: 'The Tree-Gateway config file (tree-gateway.json).' + help: 'The Tree-Gateway config file (tree-gateway.yml).' } ); diff --git a/src/config/api.ts b/src/config/api.ts index b04416ae..ccec637f 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -11,6 +11,7 @@ import { CircuitBreakerConfig, circuitBreakerConfigValidatorSchema } from './cir import { Filter, filterSchema } from './filter'; import { ValidationError } from '../error/errors'; import { MiddlewareConfig, middlewareConfigValidatorSchema } from './middleware'; +import { ObjectID } from 'bson'; /** * The API config descriptor. @@ -112,13 +113,19 @@ export let apiConfigValidatorSchema = Joi.object().keys({ version: Joi.alternatives(Joi.string(), Joi.number()).required() }); -export function validateApiConfig(apiConfig: ApiConfig) { +export function validateApiConfig(apiConfig: ApiConfig, disableApiIdValidation: boolean) { return new Promise((resolve, reject) => { Joi.validate(apiConfig, apiConfigValidatorSchema, (err, value) => { if (err) { reject(new ValidationError(err)); } else { - resolve(value); + if (disableApiIdValidation) { + return resolve(value); + } + if (value.id && !ObjectID.isValid(value.id)) { + return reject(new ValidationError(`Invalid API Id ${value.id}. The Id must be a valid ObjectID. To skip this validation, configure the 'disableApiIdValidation' property on tree-gateway.yml config file.`)); + } + return resolve(value); } }); }); diff --git a/src/config/gateway.ts b/src/config/gateway.ts index 252d9a93..35dc3712 100644 --- a/src/config/gateway.ts +++ b/src/config/gateway.ts @@ -45,6 +45,11 @@ export interface GatewayConfig { * If we are behind a reverse proxy (Heroku, Bluemix, AWS if you use an ELB, custom Nginx setup, etc) */ underProxy?: boolean; + /** + * Force the validation of any API Id. If the id is not validated, the data could not be synchronizable + * to Leanty dashboard. + */ + disableApiIdValidation?: boolean; /** * Configurations for gateway logger. */ @@ -138,6 +143,7 @@ export const gatewayConfigValidatorSchema = Joi.object().keys({ accessLogger: accessLoggerConfigSchema, admin: adminConfigValidatorSchema, cors: corsConfigSchema, + disableApiIdValidation: Joi.boolean(), errorHandler: middlewareConfigValidatorSchema, filter: Joi.array().items(middlewareConfigValidatorSchema), healthcheck: Joi.string(), diff --git a/src/gateway.ts b/src/gateway.ts index d1d3d2f8..cf874c37 100644 --- a/src/gateway.ts +++ b/src/gateway.ts @@ -272,7 +272,7 @@ export class Gateway extends EventEmitter { private loadApi(api: ApiConfig): Promise { return new Promise((resolve, reject) => { - validateApiConfig(api) + validateApiConfig(api, this.config.gateway.disableApiIdValidation) .then((value: ApiConfig) => { this.loadValidateApi(value); resolve(); diff --git a/test/unit/install-apis.spec.ts b/test/unit/install-apis.spec.ts index f18cd327..ec4f0324 100644 --- a/test/unit/install-apis.spec.ts +++ b/test/unit/install-apis.spec.ts @@ -90,6 +90,29 @@ describe('Gateway APIs install', () => { }); }); + it('should be able to reject APIs with invalid IDs', () => { + return new Promise((resolve, reject) => { + sdk.apis.addApi({ + id: 'INVALID_ID', + name: 'Invalid API', + path: '/invalid', + proxy: { + target: { + host: 'http://httpbin.org' + } + }, + version: '1.0.0' + }) + .then((apiId) => { + reject('API could not be created with invalid ID'); + }) + .catch(err => { + expect(err.statusCode).to.eq(403); + resolve(); + }); + }); + }); + it('should be able to export Gateway Configuration', () => { return new Promise((resolve, reject) => { sdk.config.get()