From 37369eb058d1a85036feab5474a640e13ac9f1c4 Mon Sep 17 00:00:00 2001 From: thiagobustamante Date: Thu, 14 Dec 2017 03:05:58 -0200 Subject: [PATCH 1/2] Register gateway version on database on startup --- package.json | 2 +- src/application.ts | 10 +++++----- src/command-line.ts | 6 +++--- src/database.ts | 14 ++++++++++++++ 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 6e6c0422..406e63e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tree-gateway", - "version": "1.3.1", + "version": "1.4.0", "homepage": "http://treegateway.org", "description": "The Tree Gateway API Gateway", "author": "Thiago da Rosa de Bustamante ", diff --git a/src/application.ts b/src/application.ts index 9b2570c9..81f49bb5 100644 --- a/src/application.ts +++ b/src/application.ts @@ -25,10 +25,9 @@ export class Application { } cluster(instances: number) { - // tslint:disable:no-console if (cluster.isMaster) { const n = instances < 1 ? os.cpus().length : instances; - console.log(`Starting child processes...`); + console.info(`Starting child processes...`); for (let i = 0; i < n; i++) { const env = { processNumber: i + 1 }; @@ -37,11 +36,11 @@ export class Application { } cluster.on('online', function(worker) { - console.log(`Child process running PID: ${worker.process.pid} PROCESS_NUMBER: ${(worker).process['env'].processNumber}`); + console.info(`Child process running PID: ${worker.process.pid} PROCESS_NUMBER: ${(worker).process['env'].processNumber}`); }); cluster.on('exit', function(worker, code, signal) { - console.log(`PID ${worker.process.pid} code: ${code} signal: ${signal}`); + console.info(`PID ${worker.process.pid} code: ${code} signal: ${signal}`); const env = (worker).process['env']; const newWorker = cluster.fork(env); (newWorker).process['env'] = env; @@ -55,7 +54,7 @@ export class Application { } process.on('uncaughtException', function(err: any) { - console.log(err); + console.error(err); }); } @@ -81,6 +80,7 @@ export class Application { } gateway.start() .then(() => gateway.startAdmin()) + .then(() => database.registerGatewayVersion()) .then(resolve) .catch(reject); diff --git a/src/command-line.ts b/src/command-line.ts index bbdde352..a8a324c4 100644 --- a/src/command-line.ts +++ b/src/command-line.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.yaml).' } ); @@ -21,8 +21,8 @@ parser.addArgument( ['-i', '--instances'], { defaultValue: 1, - type: 'int', - help: 'The number of instances to start (0 = all cpus cores)' + help: 'The number of instances to start (0 = all cpus cores)', + type: 'int' } ); diff --git a/src/database.ts b/src/database.ts index b17ae496..04d80cc5 100644 --- a/src/database.ts +++ b/src/database.ts @@ -9,6 +9,8 @@ import { Configuration } from './configuration'; @Singleton @AutoWired export class Database { + static GATEWAY_VERSION_KEY: string = '{config}:treegateway:version'; + @Inject private config: Configuration; private client: Redis.Redis; private events: Redis.Redis; @@ -31,6 +33,18 @@ export class Database { this.redisEvents.disconnect(); } + registerGatewayVersion(): Promise { + return new Promise((resolve, reject) => { + const packageJson = require('../package.json'); + this.redisClient.set(Database.GATEWAY_VERSION_KEY, `${packageJson.version}`) + .then(() => { + return resolve(); + }).catch((err: any) => { + reject(new Error('It was not possible to register the Tree Gateway version.')); + }); + }); + } + private initializeRedis(config: RedisConfig) { let client; From ff075c82fa6b3c4ef07b4faf28ae68f4c8bf627d Mon Sep 17 00:00:00 2001 From: thiagobustamante Date: Thu, 14 Dec 2017 11:21:54 -0200 Subject: [PATCH 2/2] 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()