From 20f6e8bb0d9fbafe47b244c818adbb3ea96ceec9 Mon Sep 17 00:00:00 2001 From: dnalborczyk Date: Thu, 15 Sep 2022 21:21:09 -0400 Subject: [PATCH] feat: add support for apiGateway.apiKeys (#1572) --- src/events/http/HttpServer.js | 13 +++- src/utils/getApiKeysValues.js | 7 ++ src/utils/index.js | 1 + .../apiKeys/apiGateway-apiKeys.test.js | 71 +++++++++++++++++++ .../options/apiGateway/apiKeys/serverless.yml | 30 ++++++++ tests/options/apiGateway/apiKeys/src/index.js | 10 +++ .../apiGateway/apiKeys/src/package.json | 3 + 7 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 src/utils/getApiKeysValues.js create mode 100644 tests/options/apiGateway/apiKeys/apiGateway-apiKeys.test.js create mode 100644 tests/options/apiGateway/apiKeys/serverless.yml create mode 100644 tests/options/apiGateway/apiKeys/src/index.js create mode 100644 tests/options/apiGateway/apiKeys/src/package.json diff --git a/src/events/http/HttpServer.js b/src/events/http/HttpServer.js index 039491fd4..ebb1f9ce3 100644 --- a/src/events/http/HttpServer.js +++ b/src/events/http/HttpServer.js @@ -24,6 +24,7 @@ import logRoutes from '../../utils/logRoutes.js' import { detectEncoding, generateHapiPath, + getApiKeysValues, getHttpApiCorsConfig, jsonPath, splitHandlerPathAndName, @@ -33,6 +34,8 @@ const { parse, stringify } = JSON const { assign, entries, keys } = Object export default class HttpServer { + #apiKeysValues = null + #lambda = null #options = null @@ -44,6 +47,10 @@ export default class HttpServer { #terminalInfo = [] constructor(serverless, options, lambda) { + this.#apiKeysValues = getApiKeysValues( + serverless.service.provider.apiGateway?.apiKeys ?? [], + ) + this.#lambda = lambda this.#options = options this.#serverless = serverless @@ -428,7 +435,10 @@ export default class HttpServer { const apiKey = request.headers['x-api-key'] if (apiKey) { - if (apiKey !== this.#options.apiKey) { + if ( + apiKey !== this.#options.apiKey && + !this.#apiKeysValues.has(apiKey) + ) { log.debug( `Method ${method} of function ${functionKey} token ${apiKey} not valid`, ) @@ -825,6 +835,7 @@ export default class HttpServer { const parseCookies = (headerValue) => { const cookieName = headerValue.slice(0, headerValue.indexOf('=')) const cookieValue = headerValue.slice(headerValue.indexOf('=') + 1) + h.state(cookieName, cookieValue, { encoding: 'none', strictHeader: false, diff --git a/src/utils/getApiKeysValues.js b/src/utils/getApiKeysValues.js new file mode 100644 index 000000000..2a0dea52a --- /dev/null +++ b/src/utils/getApiKeysValues.js @@ -0,0 +1,7 @@ +export default function getApiKeysValues(apiKeys) { + return new Set( + apiKeys + .filter((apiKey) => typeof apiKey === 'object' && apiKey.value != null) + .map(({ value }) => value), + ) +} diff --git a/src/utils/index.js b/src/utils/index.js index b9efa05e9..4002e92a8 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -5,6 +5,7 @@ export { default as createUniqueId } from './createUniqueId.js' export { default as detectExecutable } from './detectExecutable.js' export { default as formatToClfTime } from './formatToClfTime.js' export { default as generateHapiPath } from './generateHapiPath.js' +export { default as getApiKeysValues } from './getApiKeysValues.js' export { default as getHttpApiCorsConfig } from './getHttpApiCorsConfig.js' export { default as jsonPath } from './jsonPath.js' export { default as lowerCaseKeys } from './lowerCaseKeys.js' diff --git a/tests/options/apiGateway/apiKeys/apiGateway-apiKeys.test.js b/tests/options/apiGateway/apiKeys/apiGateway-apiKeys.test.js new file mode 100644 index 000000000..f5d61af7d --- /dev/null +++ b/tests/options/apiGateway/apiKeys/apiGateway-apiKeys.test.js @@ -0,0 +1,71 @@ +import assert from 'node:assert' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { setup, teardown } from '../../../_testHelpers/index.js' +import { BASE_URL } from '../../../config.js' + +const __dirname = dirname(fileURLToPath(import.meta.url)) + +describe('apiGateway apiKeys tests', function desc() { + beforeEach(() => + setup({ + servicePath: resolve(__dirname), + }), + ) + + afterEach(() => teardown()) + + // + ;[ + { + description: 'should ...', + expected: { + body: { + message: 'Forbidden', + }, + statusCode: 403, + }, + path: '/dev/foo', + }, + + { + description: 'should ...', + expected: { + body: { + foo: 'bar', + }, + statusCode: 200, + }, + headers: { + 'x-api-key': 'fooValuefooValuefooValue', + }, + path: '/dev/foo', + }, + + { + description: 'should ...', + expected: { + body: { + foo: 'bar', + }, + statusCode: 200, + }, + headers: { + 'x-api-key': 'barValuebarValuebarValue', + }, + path: '/dev/foo', + }, + ].forEach(({ description, expected, headers, path }) => { + it(description, async () => { + const url = new URL(path, BASE_URL) + + const response = await fetch(url, { + headers, + }) + assert.equal(response.status, expected.statusCode) + + const json = await response.json() + assert.deepEqual(json, expected.body) + }) + }) +}) diff --git a/tests/options/apiGateway/apiKeys/serverless.yml b/tests/options/apiGateway/apiKeys/serverless.yml new file mode 100644 index 000000000..355b67e47 --- /dev/null +++ b/tests/options/apiGateway/apiKeys/serverless.yml @@ -0,0 +1,30 @@ +service: apiGateway-apiKeys-tests + +configValidationMode: error +deprecationNotificationMode: error + +plugins: + - ../../../../src/index.js + +provider: + apiGateway: + apiKeys: + # - fooKeyName TODO, TEST MISSING for generated apiKey + - name: fooName + value: fooValuefooValuefooValue + - value: barValuebarValuebarValue + memorySize: 128 + name: aws + region: us-east-1 + runtime: nodejs16.x + stage: dev + versionFunctions: false + +functions: + foo: + events: + - http: + method: get + path: /foo + private: true + handler: src/index.default diff --git a/tests/options/apiGateway/apiKeys/src/index.js b/tests/options/apiGateway/apiKeys/src/index.js new file mode 100644 index 000000000..ec1726350 --- /dev/null +++ b/tests/options/apiGateway/apiKeys/src/index.js @@ -0,0 +1,10 @@ +const { stringify } = JSON + +export default async function handler() { + return { + body: stringify({ + foo: 'bar', + }), + statusCode: 200, + } +} diff --git a/tests/options/apiGateway/apiKeys/src/package.json b/tests/options/apiGateway/apiKeys/src/package.json new file mode 100644 index 000000000..3dbc1ca59 --- /dev/null +++ b/tests/options/apiGateway/apiKeys/src/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +}