Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for apiGateway.apiKeys #1572

Merged
merged 4 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/events/http/HttpServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import logRoutes from '../../utils/logRoutes.js'
import {
detectEncoding,
generateHapiPath,
getApiKeysValues,
getHttpApiCorsConfig,
jsonPath,
splitHandlerPathAndName,
Expand All @@ -33,6 +34,8 @@ const { parse, stringify } = JSON
const { assign, entries, keys } = Object

export default class HttpServer {
#apiKeysValues = null

#lambda = null

#options = null
Expand All @@ -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
Expand Down Expand Up @@ -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`,
)
Expand Down Expand Up @@ -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,
Expand Down
7 changes: 7 additions & 0 deletions src/utils/getApiKeysValues.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function getApiKeysValues(apiKeys) {
return new Set(
apiKeys
.filter((apiKey) => typeof apiKey === 'object' && apiKey.value != null)
.map(({ value }) => value),
)
}
1 change: 1 addition & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
71 changes: 71 additions & 0 deletions tests/options/apiGateway/apiKeys/apiGateway-apiKeys.test.js
Original file line number Diff line number Diff line change
@@ -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)
})
})
})
30 changes: 30 additions & 0 deletions tests/options/apiGateway/apiKeys/serverless.yml
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions tests/options/apiGateway/apiKeys/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { stringify } = JSON

export default async function handler() {
return {
body: stringify({
foo: 'bar',
}),
statusCode: 200,
}
}
3 changes: 3 additions & 0 deletions tests/options/apiGateway/apiKeys/src/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"type": "module"
}