serverless-offline 8.7.0 → 9.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +91 -95
- package/package.json +41 -69
- package/src/ServerlessOffline.js +412 -0
- package/src/config/commandOptions.js +155 -0
- package/src/config/constants.js +22 -0
- package/{dist → src}/config/defaultOptions.js +8 -17
- package/src/config/index.js +4 -0
- package/src/config/supportedRuntimes.js +47 -0
- package/src/events/authCanExecuteResource.js +35 -0
- package/src/events/authFunctionNameExtractor.js +75 -0
- package/src/events/authMatchPolicyResource.js +71 -0
- package/src/events/authValidateContext.js +51 -0
- package/src/events/http/Endpoint.js +135 -0
- package/src/events/http/Http.js +50 -0
- package/src/events/http/HttpEventDefinition.js +20 -0
- package/src/events/http/HttpServer.js +1277 -0
- package/src/events/http/OfflineEndpoint.js +33 -0
- package/src/events/http/authJWTSettingsExtractor.js +70 -0
- package/src/events/http/createAuthScheme.js +176 -0
- package/src/events/http/createJWTAuthScheme.js +106 -0
- package/src/events/http/index.js +1 -0
- package/src/events/http/javaHelpers.js +102 -0
- package/src/events/http/lambda-events/LambdaIntegrationEvent.js +57 -0
- package/src/events/http/lambda-events/LambdaProxyIntegrationEvent.js +233 -0
- package/src/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +190 -0
- package/src/events/http/lambda-events/VelocityContext.js +147 -0
- package/src/events/http/lambda-events/index.js +4 -0
- package/src/events/http/lambda-events/renderVelocityTemplateObject.js +93 -0
- package/{dist → src}/events/http/parseResources.js +73 -78
- package/src/events/http/payloadSchemaValidator.js +13 -0
- package/{dist → src}/events/http/templates/offline-default.req.vm +0 -0
- package/{dist → src}/events/http/templates/offline-default.res.vm +0 -0
- package/src/events/schedule/Schedule.js +131 -0
- package/src/events/schedule/ScheduleEvent.js +18 -0
- package/src/events/schedule/ScheduleEventDefinition.js +21 -0
- package/src/events/schedule/index.js +1 -0
- package/src/events/websocket/HttpServer.js +69 -0
- package/src/events/websocket/WebSocket.js +52 -0
- package/src/events/websocket/WebSocketClients.js +462 -0
- package/src/events/websocket/WebSocketEventDefinition.js +18 -0
- package/src/events/websocket/WebSocketServer.js +73 -0
- package/src/events/websocket/http-routes/_catchAll/catchAllRoute.js +16 -0
- package/src/events/websocket/http-routes/_catchAll/index.js +1 -0
- package/src/events/websocket/http-routes/connections/ConnectionsController.js +28 -0
- package/src/events/websocket/http-routes/connections/connectionsRoutes.js +70 -0
- package/src/events/websocket/http-routes/connections/index.js +1 -0
- package/src/events/websocket/http-routes/index.js +2 -0
- package/src/events/websocket/index.js +1 -0
- package/src/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +65 -0
- package/src/events/websocket/lambda-events/WebSocketConnectEvent.js +68 -0
- package/src/events/websocket/lambda-events/WebSocketDisconnectEvent.js +31 -0
- package/src/events/websocket/lambda-events/WebSocketEvent.js +29 -0
- package/src/events/websocket/lambda-events/WebSocketRequestContext.js +67 -0
- package/src/events/websocket/lambda-events/index.js +4 -0
- package/src/index.js +12 -0
- package/src/lambda/HttpServer.js +108 -0
- package/src/lambda/Lambda.js +68 -0
- package/src/lambda/LambdaContext.js +33 -0
- package/src/lambda/LambdaFunction.js +308 -0
- package/src/lambda/LambdaFunctionPool.js +109 -0
- package/src/lambda/__tests__/LambdaContext.test.js +30 -0
- package/src/lambda/__tests__/LambdaFunction.test.js +196 -0
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsJSONObject.fixture.js +47 -0
- package/src/lambda/__tests__/fixtures/Lambda/LambdaFunctionThatReturnsNativeString.fixture.js +46 -0
- package/src/lambda/__tests__/fixtures/Lambda/package.json +3 -0
- package/src/lambda/__tests__/fixtures/lambdaFunction.fixture.js +145 -0
- package/src/lambda/__tests__/fixtures/package.json +3 -0
- package/src/lambda/__tests__/routes/invocations/InvocationsController.test.js +42 -0
- package/src/lambda/handler-runner/HandlerRunner.js +136 -0
- package/src/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +72 -0
- package/src/lambda/handler-runner/child-process-runner/childProcessHelper.js +42 -0
- package/src/lambda/handler-runner/child-process-runner/index.js +1 -0
- package/src/lambda/handler-runner/docker-runner/DockerContainer.js +417 -0
- package/src/lambda/handler-runner/docker-runner/DockerImage.js +35 -0
- package/src/lambda/handler-runner/docker-runner/DockerRunner.js +63 -0
- package/src/lambda/handler-runner/docker-runner/index.js +1 -0
- package/src/lambda/handler-runner/go-runner/GoRunner.js +166 -0
- package/src/lambda/handler-runner/go-runner/index.js +1 -0
- package/src/lambda/handler-runner/in-process-runner/InProcessRunner.js +125 -0
- package/src/lambda/handler-runner/in-process-runner/index.js +1 -0
- package/src/lambda/handler-runner/index.js +1 -0
- package/src/lambda/handler-runner/java-runner/JavaRunner.js +114 -0
- package/src/lambda/handler-runner/java-runner/index.js +1 -0
- package/src/lambda/handler-runner/python-runner/PythonRunner.js +138 -0
- package/src/lambda/handler-runner/python-runner/index.js +1 -0
- package/{dist → src}/lambda/handler-runner/python-runner/invoke.py +0 -0
- package/src/lambda/handler-runner/ruby-runner/RubyRunner.js +107 -0
- package/src/lambda/handler-runner/ruby-runner/index.js +1 -0
- package/{dist → src}/lambda/handler-runner/ruby-runner/invoke.rb +0 -0
- package/src/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +70 -0
- package/src/lambda/handler-runner/worker-thread-runner/index.js +1 -0
- package/src/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +29 -0
- package/src/lambda/index.js +1 -0
- package/src/lambda/routes/index.js +2 -0
- package/src/lambda/routes/invocations/InvocationsController.js +102 -0
- package/src/lambda/routes/invocations/index.js +1 -0
- package/src/lambda/routes/invocations/invocationsRoute.js +77 -0
- package/src/lambda/routes/invoke-async/InvokeAsyncController.js +20 -0
- package/src/lambda/routes/invoke-async/index.js +1 -0
- package/src/lambda/routes/invoke-async/invokeAsyncRoute.js +33 -0
- package/src/utils/__tests__/createUniqueId.test.js +18 -0
- package/src/utils/__tests__/formatToClfTime.test.js +14 -0
- package/src/utils/__tests__/generateHapiPath.test.js +46 -0
- package/src/utils/__tests__/lowerCaseKeys.test.js +30 -0
- package/src/utils/__tests__/parseHeaders.test.js +13 -0
- package/src/utils/__tests__/parseMultiValueHeaders.test.js +24 -0
- package/src/utils/__tests__/parseMultiValueQueryStringParameters.test.js +159 -0
- package/src/utils/__tests__/parseQueryStringParameters.test.js +15 -0
- package/src/utils/__tests__/splitHandlerPathAndName.test.js +54 -0
- package/src/utils/__tests__/unflatten.test.js +32 -0
- package/src/utils/checkDockerDaemon.js +19 -0
- package/src/utils/checkGoVersion.js +16 -0
- package/src/utils/createApiKey.js +5 -0
- package/src/utils/createUniqueId.js +5 -0
- package/src/utils/detectExecutable.js +11 -0
- package/{dist → src}/utils/formatToClfTime.js +6 -14
- package/src/utils/generateHapiPath.js +26 -0
- package/src/utils/getHttpApiCorsConfig.js +28 -0
- package/src/utils/index.js +42 -0
- package/src/utils/jsonPath.js +13 -0
- package/src/utils/logRoutes.js +64 -0
- package/src/utils/lowerCaseKeys.js +6 -0
- package/src/utils/parseHeaders.js +14 -0
- package/src/utils/parseMultiValueHeaders.js +27 -0
- package/src/utils/parseMultiValueQueryStringParameters.js +31 -0
- package/src/utils/parseQueryStringParameters.js +15 -0
- package/src/utils/resolveJoins.js +29 -0
- package/src/utils/splitHandlerPathAndName.js +31 -0
- package/src/utils/unflatten.js +11 -0
- package/dist/ServerlessOffline.js +0 -507
- package/dist/checkEngine.js +0 -21
- package/dist/config/commandOptions.js +0 -149
- package/dist/config/constants.js +0 -30
- package/dist/config/index.js +0 -55
- package/dist/config/supportedRuntimes.js +0 -40
- package/dist/debugLog.js +0 -10
- package/dist/events/authCanExecuteResource.js +0 -35
- package/dist/events/authFunctionNameExtractor.js +0 -87
- package/dist/events/authMatchPolicyResource.js +0 -62
- package/dist/events/http/Endpoint.js +0 -171
- package/dist/events/http/Http.js +0 -77
- package/dist/events/http/HttpEventDefinition.js +0 -36
- package/dist/events/http/HttpServer.js +0 -1363
- package/dist/events/http/OfflineEndpoint.js +0 -40
- package/dist/events/http/authJWTSettingsExtractor.js +0 -76
- package/dist/events/http/authValidateContext.js +0 -48
- package/dist/events/http/createAuthScheme.js +0 -184
- package/dist/events/http/createJWTAuthScheme.js +0 -155
- package/dist/events/http/index.js +0 -15
- package/dist/events/http/javaHelpers.js +0 -99
- package/dist/events/http/lambda-events/LambdaIntegrationEvent.js +0 -85
- package/dist/events/http/lambda-events/LambdaProxyIntegrationEvent.js +0 -244
- package/dist/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +0 -221
- package/dist/events/http/lambda-events/VelocityContext.js +0 -168
- package/dist/events/http/lambda-events/index.js +0 -39
- package/dist/events/http/lambda-events/renderVelocityTemplateObject.js +0 -108
- package/dist/events/http/payloadSchemaValidator.js +0 -13
- package/dist/events/schedule/Schedule.js +0 -182
- package/dist/events/schedule/ScheduleEvent.js +0 -27
- package/dist/events/schedule/ScheduleEventDefinition.js +0 -36
- package/dist/events/schedule/index.js +0 -15
- package/dist/events/websocket/HttpServer.js +0 -112
- package/dist/events/websocket/WebSocket.js +0 -78
- package/dist/events/websocket/WebSocketClients.js +0 -550
- package/dist/events/websocket/WebSocketEventDefinition.js +0 -32
- package/dist/events/websocket/WebSocketServer.js +0 -140
- package/dist/events/websocket/http-routes/_catchAll/catchAllRoute.js +0 -33
- package/dist/events/websocket/http-routes/_catchAll/index.js +0 -15
- package/dist/events/websocket/http-routes/connections/ConnectionsController.js +0 -45
- package/dist/events/websocket/http-routes/connections/connectionsRoutes.js +0 -95
- package/dist/events/websocket/http-routes/connections/index.js +0 -15
- package/dist/events/websocket/http-routes/index.js +0 -23
- package/dist/events/websocket/index.js +0 -15
- package/dist/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +0 -99
- package/dist/events/websocket/lambda-events/WebSocketConnectEvent.js +0 -101
- package/dist/events/websocket/lambda-events/WebSocketDisconnectEvent.js +0 -47
- package/dist/events/websocket/lambda-events/WebSocketEvent.js +0 -54
- package/dist/events/websocket/lambda-events/WebSocketRequestContext.js +0 -98
- package/dist/events/websocket/lambda-events/index.js +0 -39
- package/dist/index.js +0 -19
- package/dist/lambda/HttpServer.js +0 -122
- package/dist/lambda/Lambda.js +0 -113
- package/dist/lambda/LambdaContext.js +0 -53
- package/dist/lambda/LambdaFunction.js +0 -391
- package/dist/lambda/LambdaFunctionPool.js +0 -127
- package/dist/lambda/handler-runner/HandlerRunner.js +0 -223
- package/dist/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +0 -132
- package/dist/lambda/handler-runner/child-process-runner/childProcessHelper.js +0 -40
- package/dist/lambda/handler-runner/child-process-runner/index.js +0 -15
- package/dist/lambda/handler-runner/docker-runner/DockerContainer.js +0 -517
- package/dist/lambda/handler-runner/docker-runner/DockerImage.js +0 -67
- package/dist/lambda/handler-runner/docker-runner/DockerRunner.js +0 -74
- package/dist/lambda/handler-runner/docker-runner/index.js +0 -15
- package/dist/lambda/handler-runner/go-runner/GoRunner.js +0 -211
- package/dist/lambda/handler-runner/go-runner/index.js +0 -15
- package/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js +0 -234
- package/dist/lambda/handler-runner/in-process-runner/index.js +0 -15
- package/dist/lambda/handler-runner/index.js +0 -15
- package/dist/lambda/handler-runner/java-runner/JavaRunner.js +0 -151
- package/dist/lambda/handler-runner/java-runner/index.js +0 -15
- package/dist/lambda/handler-runner/python-runner/PythonRunner.js +0 -180
- package/dist/lambda/handler-runner/python-runner/index.js +0 -15
- package/dist/lambda/handler-runner/ruby-runner/RubyRunner.js +0 -148
- package/dist/lambda/handler-runner/ruby-runner/index.js +0 -15
- package/dist/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +0 -94
- package/dist/lambda/handler-runner/worker-thread-runner/index.js +0 -15
- package/dist/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +0 -30
- package/dist/lambda/index.js +0 -15
- package/dist/lambda/routes/index.js +0 -23
- package/dist/lambda/routes/invocations/InvocationsController.js +0 -142
- package/dist/lambda/routes/invocations/index.js +0 -15
- package/dist/lambda/routes/invocations/invocationsRoute.js +0 -90
- package/dist/lambda/routes/invoke-async/InvokeAsyncController.js +0 -38
- package/dist/lambda/routes/invoke-async/index.js +0 -15
- package/dist/lambda/routes/invoke-async/invokeAsyncRoute.js +0 -43
- package/dist/main.js +0 -11
- package/dist/serverlessLog.js +0 -91
- package/dist/utils/checkDockerDaemon.js +0 -27
- package/dist/utils/checkGoVersion.js +0 -27
- package/dist/utils/createApiKey.js +0 -12
- package/dist/utils/createUniqueId.js +0 -14
- package/dist/utils/detectExecutable.js +0 -21
- package/dist/utils/generateHapiPath.js +0 -28
- package/dist/utils/getHttpApiCorsConfig.js +0 -44
- package/dist/utils/index.js +0 -158
- package/dist/utils/jsonPath.js +0 -21
- package/dist/utils/parseHeaders.js +0 -23
- package/dist/utils/parseMultiValueHeaders.js +0 -36
- package/dist/utils/parseMultiValueQueryStringParameters.js +0 -40
- package/dist/utils/parseQueryStringParameters.js +0 -26
- package/dist/utils/resolveJoins.js +0 -34
- package/dist/utils/satisfiesVersionRange.js +0 -20
- package/dist/utils/splitHandlerPathAndName.js +0 -41
- package/dist/utils/unflatten.js +0 -18
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export default class OfflineEndpoint {
|
|
2
|
+
apiKeyRequired = false
|
|
3
|
+
|
|
4
|
+
authorizationType = 'none'
|
|
5
|
+
|
|
6
|
+
authorizerFunction = false
|
|
7
|
+
|
|
8
|
+
path = ''
|
|
9
|
+
|
|
10
|
+
requestParameters = {}
|
|
11
|
+
|
|
12
|
+
requestTemplates = {
|
|
13
|
+
'application/json': '',
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
responses = {
|
|
17
|
+
default: {
|
|
18
|
+
400: {
|
|
19
|
+
statusCode: '400',
|
|
20
|
+
},
|
|
21
|
+
responseModels: {
|
|
22
|
+
'application/json;charset=UTF-8': 'Empty',
|
|
23
|
+
},
|
|
24
|
+
responseParameters: {},
|
|
25
|
+
responseTemplates: {
|
|
26
|
+
'application/json;charset=UTF-8': '',
|
|
27
|
+
},
|
|
28
|
+
statusCode: 200,
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type = 'AWS'
|
|
33
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { log } from '@serverless/utils/log.js'
|
|
2
|
+
|
|
3
|
+
export default function authJWTSettingsExtractor(
|
|
4
|
+
endpoint,
|
|
5
|
+
provider,
|
|
6
|
+
ignoreJWTSignature,
|
|
7
|
+
) {
|
|
8
|
+
const buildFailureResult = (warningMessage) => {
|
|
9
|
+
log.warning(warningMessage)
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
unsupportedAuth: true,
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const buildSuccessResult = (authorizerName) => ({ authorizerName })
|
|
17
|
+
|
|
18
|
+
const { authorizer } = endpoint
|
|
19
|
+
|
|
20
|
+
if (!authorizer) {
|
|
21
|
+
return buildSuccessResult(null)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!provider.httpApi || !provider.httpApi.authorizers) {
|
|
25
|
+
return buildSuccessResult(null)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// TODO: add code that will actually validate a JWT.
|
|
29
|
+
if (!ignoreJWTSignature) {
|
|
30
|
+
return buildSuccessResult(null)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!authorizer.name) {
|
|
34
|
+
return buildFailureResult(
|
|
35
|
+
'Serverless Offline supports only JWT authorizers referenced by name',
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const httpApiAuthorizer = provider.httpApi.authorizers[authorizer.name]
|
|
40
|
+
|
|
41
|
+
if (!httpApiAuthorizer) {
|
|
42
|
+
return buildFailureResult(`JWT authorizer ${authorizer.name} not found`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!httpApiAuthorizer.identitySource) {
|
|
46
|
+
return buildFailureResult(
|
|
47
|
+
`JWT authorizer ${authorizer.name} missing identity source`,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!httpApiAuthorizer.issuerUrl) {
|
|
52
|
+
return buildFailureResult(
|
|
53
|
+
`JWT authorizer ${authorizer.name} missing issuer url`,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!httpApiAuthorizer.audience || httpApiAuthorizer.audience.length === 0) {
|
|
58
|
+
return buildFailureResult(
|
|
59
|
+
`JWT authorizer ${authorizer.name} missing audience`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const result = {
|
|
64
|
+
authorizerName: authorizer.name,
|
|
65
|
+
...authorizer,
|
|
66
|
+
...httpApiAuthorizer,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return result
|
|
70
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import Boom from '@hapi/boom'
|
|
2
|
+
import { log } from '@serverless/utils/log.js'
|
|
3
|
+
import authCanExecuteResource from '../authCanExecuteResource.js'
|
|
4
|
+
import authValidateContext from '../authValidateContext.js'
|
|
5
|
+
import {
|
|
6
|
+
nullIfEmpty,
|
|
7
|
+
parseHeaders,
|
|
8
|
+
parseMultiValueHeaders,
|
|
9
|
+
parseMultiValueQueryStringParameters,
|
|
10
|
+
parseQueryStringParameters,
|
|
11
|
+
} from '../../utils/index.js'
|
|
12
|
+
|
|
13
|
+
export default function createAuthScheme(authorizerOptions, provider, lambda) {
|
|
14
|
+
const authFunName = authorizerOptions.name
|
|
15
|
+
let identityHeader = 'authorization'
|
|
16
|
+
|
|
17
|
+
if (authorizerOptions.type !== 'request') {
|
|
18
|
+
const identitySourceMatch = /^method.request.header.((?:\w+-?)+\w+)$/.exec(
|
|
19
|
+
authorizerOptions.identitySource,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
if (!identitySourceMatch || identitySourceMatch.length !== 2) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`Serverless Offline only supports retrieving tokens from the headers (λ: ${authFunName})`,
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
identityHeader = identitySourceMatch[1].toLowerCase()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Create Auth Scheme
|
|
32
|
+
return () => ({
|
|
33
|
+
async authenticate(request, h) {
|
|
34
|
+
log.notice()
|
|
35
|
+
log.notice(
|
|
36
|
+
`Running Authorization function for ${request.method} ${request.path} (λ: ${authFunName})`,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
// Get Authorization header
|
|
40
|
+
const { req } = request.raw
|
|
41
|
+
|
|
42
|
+
// Get path params
|
|
43
|
+
// aws doesn't auto decode path params - hapi does
|
|
44
|
+
const pathParams = { ...request.params }
|
|
45
|
+
|
|
46
|
+
const accountId = 'random-account-id'
|
|
47
|
+
const apiId = 'random-api-id'
|
|
48
|
+
const httpMethod = request.method.toUpperCase()
|
|
49
|
+
const resourcePath = request.route.path.replace(
|
|
50
|
+
new RegExp(`^/${provider.stage}`),
|
|
51
|
+
'',
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
let event = {
|
|
55
|
+
enhancedAuthContext: {},
|
|
56
|
+
methodArn: `arn:aws:execute-api:${provider.region}:${accountId}:${apiId}/${provider.stage}/${httpMethod}${resourcePath}`,
|
|
57
|
+
requestContext: {
|
|
58
|
+
accountId,
|
|
59
|
+
apiId,
|
|
60
|
+
httpMethod,
|
|
61
|
+
path: request.path,
|
|
62
|
+
requestId: 'random-request-id',
|
|
63
|
+
resourceId: 'random-resource-id',
|
|
64
|
+
resourcePath,
|
|
65
|
+
stage: provider.stage,
|
|
66
|
+
},
|
|
67
|
+
resource: resourcePath,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Create event Object for authFunction
|
|
71
|
+
// methodArn is the ARN of the function we are running we are authorizing access to (or not)
|
|
72
|
+
// Account ID and API ID are not simulated
|
|
73
|
+
if (authorizerOptions.type === 'request') {
|
|
74
|
+
const { rawHeaders, url } = req
|
|
75
|
+
|
|
76
|
+
event = {
|
|
77
|
+
...event,
|
|
78
|
+
headers: parseHeaders(rawHeaders),
|
|
79
|
+
httpMethod: request.method.toUpperCase(),
|
|
80
|
+
multiValueHeaders: parseMultiValueHeaders(rawHeaders),
|
|
81
|
+
multiValueQueryStringParameters:
|
|
82
|
+
parseMultiValueQueryStringParameters(url),
|
|
83
|
+
path: request.path,
|
|
84
|
+
pathParameters: nullIfEmpty(pathParams),
|
|
85
|
+
queryStringParameters: parseQueryStringParameters(url),
|
|
86
|
+
type: 'REQUEST',
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
const authorization = req.headers[identityHeader]
|
|
90
|
+
|
|
91
|
+
const identityValidationExpression = new RegExp(
|
|
92
|
+
authorizerOptions.identityValidationExpression,
|
|
93
|
+
)
|
|
94
|
+
const matchedAuthorization =
|
|
95
|
+
identityValidationExpression.test(authorization)
|
|
96
|
+
const finalAuthorization = matchedAuthorization ? authorization : ''
|
|
97
|
+
|
|
98
|
+
log.debug(`Retrieved ${identityHeader} header "${finalAuthorization}"`)
|
|
99
|
+
|
|
100
|
+
event = {
|
|
101
|
+
...event,
|
|
102
|
+
authorizationToken: finalAuthorization,
|
|
103
|
+
type: 'TOKEN',
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const lambdaFunction = lambda.get(authFunName)
|
|
108
|
+
lambdaFunction.setEvent(event)
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const result = await lambdaFunction.runHandler()
|
|
112
|
+
if (result === 'Unauthorized') return Boom.unauthorized('Unauthorized')
|
|
113
|
+
|
|
114
|
+
// Validate that the policy document has the principalId set
|
|
115
|
+
if (!result.principalId) {
|
|
116
|
+
log.notice(
|
|
117
|
+
`Authorization response did not include a principalId: (λ: ${authFunName})`,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return Boom.forbidden('No principalId set on the Response')
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!authCanExecuteResource(result.policyDocument, event.methodArn)) {
|
|
124
|
+
log.notice(
|
|
125
|
+
`Authorization response didn't authorize user to access resource: (λ: ${authFunName})`,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
return Boom.forbidden(
|
|
129
|
+
'User is not authorized to access this resource',
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// validate the resulting context, ensuring that all
|
|
134
|
+
// values are either string, number, or boolean types
|
|
135
|
+
if (result.context) {
|
|
136
|
+
const validationResult = authValidateContext(
|
|
137
|
+
result.context,
|
|
138
|
+
authFunName,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
if (validationResult instanceof Error) {
|
|
142
|
+
return validationResult
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
result.context = validationResult
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
log.notice(
|
|
149
|
+
`Authorization function returned a successful response: (λ: ${authFunName})`,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
const authorizer = {
|
|
153
|
+
integrationLatency: '42',
|
|
154
|
+
principalId: result.principalId,
|
|
155
|
+
...result.context,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Set the credentials for the rest of the pipeline
|
|
159
|
+
return h.authenticated({
|
|
160
|
+
credentials: {
|
|
161
|
+
authorizer,
|
|
162
|
+
context: result.context,
|
|
163
|
+
principalId: result.principalId,
|
|
164
|
+
usageIdentifierKey: result.usageIdentifierKey,
|
|
165
|
+
},
|
|
166
|
+
})
|
|
167
|
+
} catch {
|
|
168
|
+
log.notice(
|
|
169
|
+
`Authorization function returned an error response: (λ: ${authFunName})`,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
return Boom.unauthorized('Unauthorized')
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
})
|
|
176
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import Boom from '@hapi/boom'
|
|
2
|
+
import { log } from '@serverless/utils/log.js'
|
|
3
|
+
import { decode } from 'jsonwebtoken'
|
|
4
|
+
|
|
5
|
+
const { isArray } = Array
|
|
6
|
+
|
|
7
|
+
export default function createAuthScheme(jwtOptions) {
|
|
8
|
+
const authorizerName = jwtOptions.name
|
|
9
|
+
|
|
10
|
+
const identitySourceMatch = /^\$request.header.((?:\w+-?)+\w+)$/.exec(
|
|
11
|
+
jwtOptions.identitySource,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if (!identitySourceMatch || identitySourceMatch.length !== 2) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Serverless Offline only supports retrieving JWT from the headers (${authorizerName})`,
|
|
17
|
+
)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const identityHeader = identitySourceMatch[1].toLowerCase()
|
|
21
|
+
|
|
22
|
+
// Create Auth Scheme
|
|
23
|
+
return () => ({
|
|
24
|
+
async authenticate(request, h) {
|
|
25
|
+
log.notice()
|
|
26
|
+
log.notice(
|
|
27
|
+
`Running JWT Authorization function for ${request.method} ${request.path} (${authorizerName})`,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
// Get Authorization header
|
|
31
|
+
const { req } = request.raw
|
|
32
|
+
let jwtToken = req.headers[identityHeader]
|
|
33
|
+
if (jwtToken && jwtToken.split(' ')[0] === 'Bearer') {
|
|
34
|
+
;[, jwtToken] = jwtToken.split(' ')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const decoded = decode(jwtToken, { complete: true })
|
|
39
|
+
if (!decoded) {
|
|
40
|
+
return Boom.unauthorized('JWT not decoded')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const expirationDate = new Date(decoded.payload.exp * 1000)
|
|
44
|
+
if (expirationDate.valueOf() < Date.now()) {
|
|
45
|
+
return Boom.unauthorized('JWT Token expired')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const { aud, iss, scope } = decoded.payload
|
|
49
|
+
const clientId = decoded.payload.client_id
|
|
50
|
+
if (iss !== jwtOptions.issuerUrl) {
|
|
51
|
+
log.notice(`JWT Token not from correct issuer url`)
|
|
52
|
+
|
|
53
|
+
return Boom.unauthorized('JWT Token not from correct issuer url')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const validAudiences = isArray(jwtOptions.audience)
|
|
57
|
+
? jwtOptions.audience
|
|
58
|
+
: [jwtOptions.audience]
|
|
59
|
+
const providedAudiences = isArray(aud) ? aud : [aud]
|
|
60
|
+
const validAudienceProvided = providedAudiences.some((a) =>
|
|
61
|
+
validAudiences.includes(a),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if (!validAudienceProvided && !validAudiences.includes(clientId)) {
|
|
65
|
+
log.notice(`JWT Token does not contain correct audience`)
|
|
66
|
+
|
|
67
|
+
return Boom.unauthorized(
|
|
68
|
+
'JWT Token does not contain correct audience',
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let scopes = null
|
|
73
|
+
if (jwtOptions.scopes && jwtOptions.scopes.length) {
|
|
74
|
+
if (!scope) {
|
|
75
|
+
log.notice(`JWT Token missing valid scope`)
|
|
76
|
+
|
|
77
|
+
return Boom.forbidden('JWT Token missing valid scope')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
scopes = scope.split(' ')
|
|
81
|
+
if (scopes.every((s) => !jwtOptions.scopes.includes(s))) {
|
|
82
|
+
log.notice(`JWT Token missing valid scope`)
|
|
83
|
+
|
|
84
|
+
return Boom.forbidden('JWT Token missing valid scope')
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
log.notice(`JWT Token validated`)
|
|
89
|
+
|
|
90
|
+
// Set the credentials for the rest of the pipeline
|
|
91
|
+
// return resolve(
|
|
92
|
+
return h.authenticated({
|
|
93
|
+
credentials: {
|
|
94
|
+
claims: decoded.payload,
|
|
95
|
+
scopes,
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
} catch (err) {
|
|
99
|
+
log.notice(`JWT could not be decoded`)
|
|
100
|
+
log.error(err)
|
|
101
|
+
|
|
102
|
+
return Boom.unauthorized('Unauthorized')
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Http.js'
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// String functions
|
|
2
|
+
// For velocity templates to access java functions, to mimick AWS
|
|
3
|
+
|
|
4
|
+
function javaContains(value) {
|
|
5
|
+
return this.includes(value)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function javaEquals(anObject) {
|
|
9
|
+
return this.toString() === anObject.toString()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function javaEqualsIgnoreCase(anotherString) {
|
|
13
|
+
return anotherString === null
|
|
14
|
+
? false
|
|
15
|
+
: this === anotherString ||
|
|
16
|
+
this.toLowerCase() === anotherString.toLowerCase()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function javaMatches(value) {
|
|
20
|
+
return this.match(new RegExp(value, 'm'))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function javaReplaceAll(oldValue, newValue) {
|
|
24
|
+
return this.replace(new RegExp(oldValue, 'gm'), newValue)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function javaReplaceFirst(oldValue, newValue) {
|
|
28
|
+
return this.replace(new RegExp(oldValue, 'm'), newValue)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// method has 2 function signatures:
|
|
32
|
+
// regionMatches(toffset: number, other: string, ooffset: number, len: number): boolean
|
|
33
|
+
// regionMatches(ignoreCase: boolean, toffset: number, other: string, ooffset: number, len: number): boolean
|
|
34
|
+
function javaRegionMatches(...args) {
|
|
35
|
+
let ignoreCase
|
|
36
|
+
let toffset
|
|
37
|
+
let other
|
|
38
|
+
let ooffset
|
|
39
|
+
let len
|
|
40
|
+
|
|
41
|
+
if (args.length === 4) {
|
|
42
|
+
;[toffset, other, ooffset, len] = args
|
|
43
|
+
ignoreCase = false
|
|
44
|
+
} else {
|
|
45
|
+
;[ignoreCase, toffset, other, ooffset, len] = args
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Note: toffset, ooffset, or len might be near -1>>>1.
|
|
49
|
+
if (
|
|
50
|
+
ooffset < 0 ||
|
|
51
|
+
toffset < 0 ||
|
|
52
|
+
toffset > this.length - len ||
|
|
53
|
+
ooffset > other.length - len
|
|
54
|
+
) {
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let s1 = this.substring(toffset, toffset + len)
|
|
59
|
+
let s2 = other.substring(ooffset, ooffset + len)
|
|
60
|
+
|
|
61
|
+
if (ignoreCase) {
|
|
62
|
+
s1 = s1.toLowerCase()
|
|
63
|
+
s2 = s2.toLowerCase()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return s1 === s2
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const {
|
|
70
|
+
prototype,
|
|
71
|
+
prototype: {
|
|
72
|
+
contains,
|
|
73
|
+
equals,
|
|
74
|
+
equalsIgnoreCase,
|
|
75
|
+
matches,
|
|
76
|
+
regionMatches,
|
|
77
|
+
replaceAll,
|
|
78
|
+
replaceFirst,
|
|
79
|
+
},
|
|
80
|
+
} = String
|
|
81
|
+
|
|
82
|
+
export default function runInPollutedScope(runScope) {
|
|
83
|
+
prototype.contains = javaContains
|
|
84
|
+
prototype.equals = javaEquals
|
|
85
|
+
prototype.equalsIgnoreCase = javaEqualsIgnoreCase
|
|
86
|
+
prototype.matches = javaMatches
|
|
87
|
+
prototype.regionMatches = javaRegionMatches
|
|
88
|
+
prototype.replaceAll = javaReplaceAll
|
|
89
|
+
prototype.replaceFirst = javaReplaceFirst
|
|
90
|
+
|
|
91
|
+
const result = runScope()
|
|
92
|
+
|
|
93
|
+
prototype.contains = contains
|
|
94
|
+
prototype.equals = equals
|
|
95
|
+
prototype.equalsIgnoreCase = equalsIgnoreCase
|
|
96
|
+
prototype.matches = matches
|
|
97
|
+
prototype.regionMatches = regionMatches
|
|
98
|
+
prototype.replaceAll = replaceAll
|
|
99
|
+
prototype.replaceFirst = replaceFirst
|
|
100
|
+
|
|
101
|
+
return result
|
|
102
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { env } from 'node:process'
|
|
2
|
+
import { log } from '@serverless/utils/log.js'
|
|
3
|
+
import renderVelocityTemplateObject from './renderVelocityTemplateObject.js'
|
|
4
|
+
import VelocityContext from './VelocityContext.js'
|
|
5
|
+
|
|
6
|
+
const { parse } = JSON
|
|
7
|
+
|
|
8
|
+
export default class LambdaIntegrationEvent {
|
|
9
|
+
#path = null
|
|
10
|
+
|
|
11
|
+
#request = null
|
|
12
|
+
|
|
13
|
+
#requestTemplate = null
|
|
14
|
+
|
|
15
|
+
#stage = null
|
|
16
|
+
|
|
17
|
+
constructor(request, stage, requestTemplate, path) {
|
|
18
|
+
this.#path = path
|
|
19
|
+
this.#request = request
|
|
20
|
+
this.#requestTemplate = requestTemplate
|
|
21
|
+
this.#stage = stage
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
create() {
|
|
25
|
+
if (env.AUTHORIZER) {
|
|
26
|
+
try {
|
|
27
|
+
const authorizerContext = parse(env.AUTHORIZER)
|
|
28
|
+
if (authorizerContext) {
|
|
29
|
+
this.#request.auth = {
|
|
30
|
+
...this.#request.auth,
|
|
31
|
+
credentials: {
|
|
32
|
+
authorizer: authorizerContext,
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} catch {
|
|
37
|
+
log.error(
|
|
38
|
+
'Could not parse process.env.AUTHORIZER, make sure it is correct JSON',
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const velocityContext = new VelocityContext(
|
|
44
|
+
this.#request,
|
|
45
|
+
this.#stage,
|
|
46
|
+
this.#request.payload || {},
|
|
47
|
+
this.#path,
|
|
48
|
+
).getContext()
|
|
49
|
+
|
|
50
|
+
const event = renderVelocityTemplateObject(
|
|
51
|
+
this.#requestTemplate,
|
|
52
|
+
velocityContext,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return event
|
|
56
|
+
}
|
|
57
|
+
}
|