From e311989c93e7f7079f1fe1699026c964975dc56a Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Mon, 15 Jan 2024 17:00:07 +0330 Subject: [PATCH 1/3] add api_key middleware for sign route --- config/default.yaml | 1 + src/api/signTx.ts | 4 ++++ src/configs/Configs.ts | 1 + src/jobs/apiServer.ts | 14 +++++++++++++- src/utils/authentication.ts | 24 ++++++++++++++++++++++++ 5 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 src/utils/authentication.ts diff --git a/config/default.yaml b/config/default.yaml index e3ab3af6..2c6318df 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -4,6 +4,7 @@ api: port: 8080 # service deployment port host: 'localhost' # service listening host + apiKeyHash: '' # blake2b hash of API_KEY jsonBodyLimit: 50 isManualTxRequestActive: false diff --git a/src/api/signTx.ts b/src/api/signTx.ts index c9ddac70..4678a886 100644 --- a/src/api/signTx.ts +++ b/src/api/signTx.ts @@ -9,6 +9,7 @@ import DatabaseHandler from '../db/DatabaseHandler'; import WinstonLogger from '@rosen-bridge/winston-logger'; import { DuplicateTransaction } from '../utils/errors'; import GuardPkHandler from '../handlers/GuardPkHandler'; +import { authenticateKey } from '../utils/authentication'; const logger = WinstonLogger.getInstance().getLogger(import.meta.url); @@ -25,8 +26,11 @@ const signTxRoute = (server: FastifySeverInstance) => { response: { 200: MessageResponseSchema, 400: MessageResponseSchema, + 403: MessageResponseSchema, }, + security: [{ apiKey: [] }], }, + preHandler: [authenticateKey], }, async (request, reply) => { const { chain, txJson, requiredSign, overwrite } = request.body; diff --git a/src/configs/Configs.ts b/src/configs/Configs.ts index ed3cbf7e..5aecd5e4 100644 --- a/src/configs/Configs.ts +++ b/src/configs/Configs.ts @@ -66,6 +66,7 @@ class Configs { // express config static apiPort = getConfigIntKeyOrDefault('api.port', 8080); static apiHost = getOptionalConfig('api.host', 'localhost'); + static apiKeyHash = config.get('api.apiKeyHash'); static apiBodyLimit = getConfigIntKeyOrDefault('api.jsonBodyLimit', 50) * 1024 * 1024; // value in MB static isManualTxRequestActive = getOptionalConfig( diff --git a/src/jobs/apiServer.ts b/src/jobs/apiServer.ts index 8cdbf3ee..96aed9b6 100644 --- a/src/jobs/apiServer.ts +++ b/src/jobs/apiServer.ts @@ -29,7 +29,19 @@ const initApiServer = async () => { bodyLimit: Configs.apiBodyLimit, }).withTypeProvider(); - await apiServer.register(swagger); + await apiServer.register(swagger, { + openapi: { + components: { + securitySchemes: { + apiKey: { + type: 'apiKey', + name: 'api_key', + in: 'header', + }, + }, + }, + }, + }); await apiServer.register(fastifyCors, {}); await apiServer.register(swaggerUi, { diff --git a/src/utils/authentication.ts b/src/utils/authentication.ts new file mode 100644 index 00000000..ff6ca105 --- /dev/null +++ b/src/utils/authentication.ts @@ -0,0 +1,24 @@ +import { blake2b } from 'blakejs'; +import { Buffer } from 'buffer'; +import Configs from '../configs/Configs'; +import { FastifyReply, FastifyRequest } from 'fastify'; +import { HookHandlerDoneFunction } from 'fastify/types/hooks'; + +const authenticateKey = ( + req: T, + res: U, + next: HookHandlerDoneFunction +) => { + const api_key: string = req.headers.api_key as string; + if ( + api_key && + Buffer.from(blake2b(api_key, undefined, 32)).toString('hex') == + Configs.apiKeyHash + ) { + next(); + } else { + res.status(403).send({ message: "API_KEY doesn't exist or it's wrong" }); + } +}; + +export { authenticateKey }; From 7ef766a1fd51e024b7d4266e31c62eed69bb5112 Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Mon, 15 Jan 2024 17:25:12 +0330 Subject: [PATCH 2/3] fix unit test for sign route --- config/test.yaml | 1 + tests/api/signTx.spec.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/config/test.yaml b/config/test.yaml index 19a7ab71..4ece3673 100644 --- a/config/test.yaml +++ b/config/test.yaml @@ -1,6 +1,7 @@ --- api: isManualTxRequestActive: true + apiKeyHash: '324dcf027dd4a30a932c441f365a25e86b173defa4b8e58948253471b81b72cf' cardano: networkType: 'testnet' chainNetwork: 'koios' diff --git a/tests/api/signTx.spec.ts b/tests/api/signTx.spec.ts index 08951a9c..e3d22b0d 100644 --- a/tests/api/signTx.spec.ts +++ b/tests/api/signTx.spec.ts @@ -68,6 +68,9 @@ describe('signTx', () => { txJson: 'txJson', requiredSign: requiredSign, }, + headers: { + API_KEY: 'hello', + }, }); // check the result @@ -123,6 +126,9 @@ describe('signTx', () => { txJson: 'txJson', requiredSign: requiredSign, }, + headers: { + API_KEY: 'hello', + }, }); // check the result @@ -185,6 +191,9 @@ describe('signTx', () => { txJson: 'txJson', requiredSign: requiredSign, }, + headers: { + API_KEY: 'hello', + }, }); // check the result @@ -248,6 +257,9 @@ describe('signTx', () => { requiredSign: newRequiredSign, overwrite: true, }, + headers: { + API_KEY: 'hello', + }, }); // check the result @@ -302,10 +314,38 @@ describe('signTx', () => { txJson: 'txJson', requiredSign: GuardPkHandler.getInstance().guardsLen + 1, }, + headers: { + API_KEY: 'hello', + }, }); // check the result expect(result.statusCode).toEqual(400); }); + + /** + * @target fastifyServer[POST /sign] should return 403 when api_key did not set in header + * @dependencies + * @scenario + * - send a request to the server + * - check the result + * @expected + * - it should return status code 403 + */ + it('should return 403 when api_key did not set in header', async () => { + // send a request to the server + const result = await mockedServer.inject({ + method: 'POST', + url: '/sign', + body: { + chain: CARDANO_CHAIN, + txJson: 'txJson', + requiredSign: GuardPkHandler.getInstance().guardsLen, + }, + }); + + // check the result + expect(result.statusCode).toEqual(403); + }); }); }); From dc3c0c70b0609c4e6fc90ad866b0d68cfd488d7d Mon Sep 17 00:00:00 2001 From: Moein zargarzadeh Date: Mon, 15 Jan 2024 17:51:17 +0330 Subject: [PATCH 3/3] add unit tests for wrong api_key sign route --- tests/api/signTx.spec.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/api/signTx.spec.ts b/tests/api/signTx.spec.ts index e3d22b0d..6f1398c2 100644 --- a/tests/api/signTx.spec.ts +++ b/tests/api/signTx.spec.ts @@ -343,7 +343,33 @@ describe('signTx', () => { requiredSign: GuardPkHandler.getInstance().guardsLen, }, }); + // check the result + expect(result.statusCode).toEqual(403); + }); + /** + * @target fastifyServer[POST /sign] should return 403 when api_key is wrong + * @dependencies + * @scenario + * - send a request to the server + * - check the result + * @expected + * - it should return status code 403 + */ + it('should return 403 when api_key is wrong', async () => { + // send a request to the server + const result = await mockedServer.inject({ + method: 'POST', + url: '/sign', + body: { + chain: CARDANO_CHAIN, + txJson: 'txJson', + requiredSign: GuardPkHandler.getInstance().guardsLen, + }, + headers: { + API_KEY: 'wrong', + }, + }); // check the result expect(result.statusCode).toEqual(403); });