lemon-core 4.1.14 → 4.2.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/dist/common/test-helper.d.ts +2 -2
- package/dist/common/test-helper.js +24 -26
- package/dist/common/test-helper.js.map +1 -1
- package/dist/controllers/dummy-controller.js +39 -46
- package/dist/controllers/dummy-controller.js.map +1 -1
- package/dist/controllers/general-api-controller.js +95 -100
- package/dist/controllers/general-api-controller.js.map +1 -1
- package/dist/controllers/general-controller.js +81 -82
- package/dist/controllers/general-controller.js.map +1 -1
- package/dist/cores/api/api-service.d.ts +1 -1
- package/dist/cores/api/api-service.js +228 -269
- package/dist/cores/api/api-service.js.map +1 -1
- package/dist/cores/aws/aws-kms-service.d.ts +1 -2
- package/dist/cores/aws/aws-kms-service.js +143 -153
- package/dist/cores/aws/aws-kms-service.js.map +1 -1
- package/dist/cores/aws/aws-s3-service.d.ts +2 -4
- package/dist/cores/aws/aws-s3-service.js +306 -330
- package/dist/cores/aws/aws-s3-service.js.map +1 -1
- package/dist/cores/aws/aws-sns-service.js +147 -153
- package/dist/cores/aws/aws-sns-service.js.map +1 -1
- package/dist/cores/aws/aws-sqs-service.js +149 -170
- package/dist/cores/aws/aws-sqs-service.js.map +1 -1
- package/dist/cores/aws/index.js +10 -20
- package/dist/cores/aws/index.js.map +1 -1
- package/dist/cores/cache/cache-service.d.ts +2 -2
- package/dist/cores/cache/cache-service.js +435 -499
- package/dist/cores/cache/cache-service.js.map +1 -1
- package/dist/cores/config/config-service.d.ts +1 -1
- package/dist/cores/config/config-service.js +56 -63
- package/dist/cores/config/config-service.js.map +1 -1
- package/dist/cores/config/index.js +14 -24
- package/dist/cores/config/index.js.map +1 -1
- package/dist/cores/core-services.d.ts +1 -1
- package/dist/cores/dynamo/dynamo-query-service.js +37 -51
- package/dist/cores/dynamo/dynamo-query-service.js.map +1 -1
- package/dist/cores/dynamo/dynamo-scan-service.d.ts +2 -2
- package/dist/cores/dynamo/dynamo-scan-service.js +29 -40
- package/dist/cores/dynamo/dynamo-scan-service.js.map +1 -1
- package/dist/cores/dynamo/dynamo-service.d.ts +2 -2
- package/dist/cores/dynamo/dynamo-service.js +528 -601
- package/dist/cores/dynamo/dynamo-service.js.map +1 -1
- package/dist/cores/dynamo/tools/expressions.js +17 -7
- package/dist/cores/dynamo/tools/expressions.js.map +1 -1
- package/dist/cores/dynamo/tools/query.js +142 -127
- package/dist/cores/dynamo/tools/query.js.map +1 -1
- package/dist/cores/dynamo/tools/scan.js +111 -97
- package/dist/cores/dynamo/tools/scan.js.map +1 -1
- package/dist/cores/dynamo/tools/serializer.js +17 -7
- package/dist/cores/dynamo/tools/serializer.js.map +1 -1
- package/dist/cores/dynamo/tools/utils.d.ts +0 -2
- package/dist/cores/dynamo/tools/utils.js.map +1 -1
- package/dist/cores/elastic/elastic6-query-service.js +307 -324
- package/dist/cores/elastic/elastic6-query-service.js.map +1 -1
- package/dist/cores/elastic/elastic6-service.d.ts +3 -3
- package/dist/cores/elastic/elastic6-service.js +568 -647
- package/dist/cores/elastic/elastic6-service.js.map +1 -1
- package/dist/cores/elastic/hangul-service.js +52 -54
- package/dist/cores/elastic/hangul-service.js.map +1 -1
- package/dist/cores/lambda/index.js +42 -36
- package/dist/cores/lambda/index.js.map +1 -1
- package/dist/cores/lambda/lambda-alb-handler.d.ts +2 -2
- package/dist/cores/lambda/lambda-alb-handler.js +59 -72
- package/dist/cores/lambda/lambda-alb-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-cognito-handler.js +10 -19
- package/dist/cores/lambda/lambda-cognito-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-cron-handler.d.ts +1 -1
- package/dist/cores/lambda/lambda-cron-handler.js +14 -23
- package/dist/cores/lambda/lambda-cron-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-dynamo-stream-handler.d.ts +2 -2
- package/dist/cores/lambda/lambda-dynamo-stream-handler.js +57 -67
- package/dist/cores/lambda/lambda-dynamo-stream-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-handler.d.ts +22 -22
- package/dist/cores/lambda/lambda-handler.js +93 -106
- package/dist/cores/lambda/lambda-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-notification-handler.d.ts +1 -1
- package/dist/cores/lambda/lambda-notification-handler.js +39 -50
- package/dist/cores/lambda/lambda-notification-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-sns-handler.js +79 -88
- package/dist/cores/lambda/lambda-sns-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-sqs-handler.js +79 -88
- package/dist/cores/lambda/lambda-sqs-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-web-handler.d.ts +21 -43
- package/dist/cores/lambda/lambda-web-handler.js +392 -482
- package/dist/cores/lambda/lambda-web-handler.js.map +1 -1
- package/dist/cores/lambda/lambda-wss-handler.js +23 -32
- package/dist/cores/lambda/lambda-wss-handler.js.map +1 -1
- package/dist/cores/protocol/index.js +10 -20
- package/dist/cores/protocol/index.js.map +1 -1
- package/dist/cores/protocol/protocol-service.d.ts +3 -3
- package/dist/cores/protocol/protocol-service.js +235 -227
- package/dist/cores/protocol/protocol-service.js.map +1 -1
- package/dist/cores/storage/http-storage-service.js +65 -85
- package/dist/cores/storage/http-storage-service.js.map +1 -1
- package/dist/cores/storage/model-manager.js +66 -85
- package/dist/cores/storage/model-manager.js.map +1 -1
- package/dist/cores/storage/proxy-storage-service.d.ts +2 -2
- package/dist/cores/storage/proxy-storage-service.js +562 -599
- package/dist/cores/storage/proxy-storage-service.js.map +1 -1
- package/dist/cores/storage/redis-storage-service.js +163 -177
- package/dist/cores/storage/redis-storage-service.js.map +1 -1
- package/dist/cores/storage/storage-service.js +281 -322
- package/dist/cores/storage/storage-service.js.map +1 -1
- package/dist/engine/builder.d.ts +1 -1
- package/dist/engine/builder.js +59 -63
- package/dist/engine/builder.js.map +1 -1
- package/dist/engine/engine.d.ts +4 -4
- package/dist/engine/engine.js +9 -18
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/types.d.ts +1 -1
- package/dist/engine/utilities.d.ts +14 -10
- package/dist/engine/utilities.js +301 -280
- package/dist/engine/utilities.js.map +1 -1
- package/dist/environ.js +4 -6
- package/dist/environ.js.map +1 -1
- package/dist/extended/abstract-service.js +595 -645
- package/dist/extended/abstract-service.js.map +1 -1
- package/dist/extended/libs/sig-v4.js.map +1 -1
- package/dist/generated/field-registry.d.ts +10 -0
- package/dist/generated/field-registry.js +17 -0
- package/dist/generated/field-registry.js.map +1 -0
- package/dist/helpers/helpers.d.ts +17 -9
- package/dist/helpers/helpers.js +88 -78
- package/dist/helpers/helpers.js.map +1 -1
- package/dist/index.js +17 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/dynamodb-value.js +2 -3
- package/dist/lib/dynamodb-value.js.map +1 -1
- package/dist/tools/express.js +4 -5
- package/dist/tools/express.js.map +1 -1
- package/dist/tools/tools.d.ts +3 -1
- package/dist/tools/tools.js +14 -21
- package/dist/tools/tools.js.map +1 -1
- package/package.json +19 -18
- package/dist/exec-cli.d.ts +0 -2
- package/dist/exec-cli.js +0 -211
- package/dist/exec-cli.js.map +0 -1
- package/dist/lib/dynamo/expressions.d.ts +0 -14
- package/dist/lib/dynamo/expressions.js +0 -212
- package/dist/lib/dynamo/expressions.js.map +0 -1
- package/dist/lib/dynamo/query.d.ts +0 -43
- package/dist/lib/dynamo/query.js +0 -246
- package/dist/lib/dynamo/query.js.map +0 -1
- package/dist/lib/dynamo/scan.d.ts +0 -33
- package/dist/lib/dynamo/scan.js +0 -172
- package/dist/lib/dynamo/scan.js.map +0 -1
- package/dist/lib/dynamo/serializer.d.ts +0 -12
- package/dist/lib/dynamo/serializer.js +0 -243
- package/dist/lib/dynamo/serializer.js.map +0 -1
- package/dist/lib/dynamo/utils.d.ts +0 -15
- package/dist/lib/dynamo/utils.js +0 -129
- package/dist/lib/dynamo/utils.js.map +0 -1
- package/dist/tools/shared.d.ts +0 -37
- package/dist/tools/shared.js +0 -130
- package/dist/tools/shared.js.map +0 -1
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
14
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
6
|
+
exports.MyHttpHeaderTool = exports.LambdaWEBHandler = exports.mxNextFailure = exports.mxNextHandler = exports.promised = exports.redirect = exports.failure = exports.notfound = exports.success = exports.buildResponse = void 0;
|
|
16
7
|
/**
|
|
17
8
|
* `lambda-web-handler.ts`
|
|
18
9
|
* - lambda handler to process WEB(API) event.
|
|
@@ -37,6 +28,7 @@ const test_helper_1 = require("../../common/test-helper");
|
|
|
37
28
|
const tools_1 = require("../../tools/");
|
|
38
29
|
const protocol_service_1 = require("../protocol/protocol-service");
|
|
39
30
|
const aws_kms_service_1 = require("../aws/aws-kms-service");
|
|
31
|
+
const client_sts_1 = require("@aws-sdk/client-sts");
|
|
40
32
|
const config_1 = __importDefault(require("../config"));
|
|
41
33
|
const protocol_1 = __importDefault(require("../protocol/"));
|
|
42
34
|
const NS = engine_1.$U.NS('HWEB', 'yellow'); // NAMESPACE TO BE PRINTED.
|
|
@@ -64,9 +56,9 @@ const $kmsPool = {};
|
|
|
64
56
|
* @returns http response body
|
|
65
57
|
*/
|
|
66
58
|
const buildResponse = (statusCode, body, options) => {
|
|
67
|
-
const contentType = options
|
|
68
|
-
const origin =
|
|
69
|
-
const credentials =
|
|
59
|
+
const contentType = options?.contentType;
|
|
60
|
+
const origin = options?.origin === undefined ? '*' : options?.origin;
|
|
61
|
+
const credentials = options?.credentials === undefined ? true : options?.credentials;
|
|
70
62
|
const isBase64Encoded = contentType && !contentType.startsWith('text/') ? true : false;
|
|
71
63
|
const _isXml = (body) => body.startsWith('<?xml ') && body.trim().endsWith('>');
|
|
72
64
|
const _isHtml = (body) => body.startsWith('<!DOCTYPE html>') || (body.startsWith('<') && body.endsWith('>'));
|
|
@@ -121,28 +113,28 @@ exports.redirect = redirect;
|
|
|
121
113
|
* @param event event
|
|
122
114
|
* @param $ctx context
|
|
123
115
|
*/
|
|
124
|
-
const promised = (event, $ctx) =>
|
|
116
|
+
const promised = async (event, $ctx) => {
|
|
125
117
|
// TO SERVE BINARY. `$ npm i -S serverless-apigw-binary serverless-apigwy-binary`. refer 'https://read.acloud.guru/serverless-image-optimization-and-delivery-510b6c311fe5'
|
|
126
118
|
if (event && event.httpMethod == 'GET' && event.path == '/favicon.ico') {
|
|
127
119
|
return () => (0, exports.success)(FAVICON_ICO, 'image/x-icon');
|
|
128
120
|
}
|
|
129
121
|
//* transform to protocol-context.
|
|
130
122
|
const param = protocol_1.default.service.asTransformer('web').transformToParam(event, $ctx);
|
|
131
|
-
(0, engine_1._log)(NS, '! protocol-param =', engine_1.$U.json(
|
|
123
|
+
(0, engine_1._log)(NS, '! protocol-param =', engine_1.$U.json({ ...param, body: undefined })); // hide `.body` in log.
|
|
132
124
|
//* returns object..
|
|
133
125
|
return { event, param, $ctx };
|
|
134
|
-
}
|
|
126
|
+
};
|
|
135
127
|
exports.promised = promised;
|
|
136
128
|
/**
|
|
137
129
|
* builder for default handler
|
|
138
130
|
*/
|
|
139
|
-
const mxNextHandler = (thiz) => (params) =>
|
|
131
|
+
const mxNextHandler = (thiz) => async (params) => {
|
|
140
132
|
//* determine if param or func.
|
|
141
133
|
const fx = typeof params == 'function' ? params : null;
|
|
142
134
|
const $param = params && typeof params == 'object' ? params : null;
|
|
143
135
|
const { param, event } = $param || {};
|
|
144
136
|
//* call the main handler()
|
|
145
|
-
const R = $param ?
|
|
137
|
+
const R = $param ? await thiz.handleProtocol(param, event) : fx;
|
|
146
138
|
//* - if like to override the full response, then return function.
|
|
147
139
|
if (R && typeof R == 'function')
|
|
148
140
|
return R();
|
|
@@ -154,7 +146,7 @@ const mxNextHandler = (thiz) => (params) => __awaiter(void 0, void 0, void 0, fu
|
|
|
154
146
|
}
|
|
155
147
|
//* returns response..
|
|
156
148
|
return (0, exports.success)(R);
|
|
157
|
-
}
|
|
149
|
+
};
|
|
158
150
|
exports.mxNextHandler = mxNextHandler;
|
|
159
151
|
/**
|
|
160
152
|
* builder for failure promised.
|
|
@@ -214,36 +206,15 @@ exports.mxNextFailure = mxNextFailure;
|
|
|
214
206
|
* - default WEB Handler w/ event-listeners.
|
|
215
207
|
*/
|
|
216
208
|
class LambdaWEBHandler extends lambda_handler_1.LambdaSubHandler {
|
|
209
|
+
//* shared config.
|
|
210
|
+
static REPORT_ERROR = lambda_handler_1.LambdaHandler.REPORT_ERROR;
|
|
211
|
+
//* handlers map.
|
|
212
|
+
_handlers = {};
|
|
217
213
|
/**
|
|
218
214
|
* default constructor w/ registering self.
|
|
219
215
|
*/
|
|
220
216
|
constructor(lambda, register) {
|
|
221
217
|
super(lambda, register ? 'web' : undefined);
|
|
222
|
-
//* handlers map.
|
|
223
|
-
this._handlers = {};
|
|
224
|
-
/**
|
|
225
|
-
* Default WEB Handler.
|
|
226
|
-
*/
|
|
227
|
-
this.handle = (event, $ctx) => __awaiter(this, void 0, void 0, function* () {
|
|
228
|
-
//* inspect API parameters.
|
|
229
|
-
(0, engine_1._log)(NS, `handle()....`);
|
|
230
|
-
const $path = event.pathParameters || {};
|
|
231
|
-
const $param = event.queryStringParameters || {};
|
|
232
|
-
(0, engine_1._log)(NS, '! path =', event.path);
|
|
233
|
-
(0, engine_1._log)(NS, '! $path =', engine_1.$U.json($path));
|
|
234
|
-
(0, engine_1._log)(NS, '! $param =', engine_1.$U.json($param));
|
|
235
|
-
//* start promised..
|
|
236
|
-
return (0, exports.promised)(event, $ctx).then((0, exports.mxNextHandler)(this)).catch((0, exports.mxNextFailure)(event, $ctx));
|
|
237
|
-
});
|
|
238
|
-
/**
|
|
239
|
-
* builder of tools for http-headers
|
|
240
|
-
* - extracting header content, and parse.
|
|
241
|
-
*/
|
|
242
|
-
this.tools = (headers) => new MyHttpHeaderTool(headers);
|
|
243
|
-
/**
|
|
244
|
-
* builder of tools without kms.
|
|
245
|
-
*/
|
|
246
|
-
this.toolsV2 = (headers, reqContext) => new MyHttpHeaderToolV2(headers, reqContext);
|
|
247
218
|
// _log(NS, `LambdaWEBHandler()..`);
|
|
248
219
|
}
|
|
249
220
|
/**
|
|
@@ -288,154 +259,202 @@ class LambdaWEBHandler extends lambda_handler_1.LambdaSubHandler {
|
|
|
288
259
|
return M;
|
|
289
260
|
}, {});
|
|
290
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Default WEB Handler.
|
|
264
|
+
*/
|
|
265
|
+
handle = async (event, $ctx) => {
|
|
266
|
+
//* inspect API parameters.
|
|
267
|
+
(0, engine_1._log)(NS, `handle()....`);
|
|
268
|
+
const $path = event.pathParameters || {};
|
|
269
|
+
const $param = event.queryStringParameters || {};
|
|
270
|
+
(0, engine_1._log)(NS, '! path =', event.path);
|
|
271
|
+
(0, engine_1._log)(NS, '! $path =', engine_1.$U.json($path));
|
|
272
|
+
(0, engine_1._log)(NS, '! $param =', engine_1.$U.json($param));
|
|
273
|
+
//* start promised..
|
|
274
|
+
return (0, exports.promised)(event, $ctx).then((0, exports.mxNextHandler)(this)).catch((0, exports.mxNextFailure)(event, $ctx));
|
|
275
|
+
};
|
|
291
276
|
/**
|
|
292
277
|
* handle param via protocol-service.
|
|
293
278
|
*
|
|
294
279
|
* @param param protocol parameters
|
|
295
280
|
* @param event (optional) origin event object.
|
|
296
281
|
*/
|
|
297
|
-
handleProtocol(param, event) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
282
|
+
async handleProtocol(param, event) {
|
|
283
|
+
if (!param)
|
|
284
|
+
throw new Error(`@param (protocol-param) is required!`);
|
|
285
|
+
//TODO [Steve] id, cmd can (or should) be null or empty! (support for `/{cmd+}` pattern) @251212
|
|
286
|
+
const TYPE = `${param.type || ''}`;
|
|
287
|
+
const MODE = `${param.mode || 'GET'}`;
|
|
288
|
+
const ID = `${param.id || ''}`;
|
|
289
|
+
const CMD = `${param.cmd || ''}`;
|
|
290
|
+
const PATH = `${(event && event.path) || ''}`;
|
|
291
|
+
const $param = param.param;
|
|
292
|
+
const $body = param.body;
|
|
293
|
+
const context = param.context;
|
|
294
|
+
//* debug print body.
|
|
295
|
+
if (!$body) {
|
|
296
|
+
(0, engine_1._log)(NS, `#${MODE}:${CMD} (${TYPE}/${ID})....`);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
(0, engine_1._log)(NS, `#${MODE}:${CMD} (${TYPE}/${ID}).... body.len=`, $body ? engine_1.$U.json($body).length : -1);
|
|
300
|
+
}
|
|
301
|
+
//* find target next function
|
|
302
|
+
// const decoder: NextDecoder | CoreWEBController = this._handlers[TYPE];
|
|
303
|
+
const next = ((decoder) => {
|
|
304
|
+
//* as default handler '/', say the current version.
|
|
305
|
+
if (MODE === 'LIST' && TYPE === '' && ID === '' && CMD === '') {
|
|
306
|
+
return async () => {
|
|
307
|
+
const $pack = (0, tools_1.loadJsonSync)('package.json');
|
|
308
|
+
const name = ($pack && $pack.name) || 'LEMON API';
|
|
309
|
+
const version = ($pack && $pack.version) || '0.0.0';
|
|
310
|
+
const modules = [`${name}/${version}`];
|
|
311
|
+
//* shows version of `lemon-core` via `dependencies`.
|
|
312
|
+
const coreVer = $pack && $pack.dependencies && $pack.dependencies['lemon-core'];
|
|
313
|
+
if (coreVer)
|
|
314
|
+
modules.push(`lemon-core/${coreVer.startsWith('^') ? coreVer.substring(1) : coreVer}`);
|
|
315
|
+
return modules.join('\n');
|
|
316
|
+
};
|
|
316
317
|
}
|
|
317
|
-
//*
|
|
318
|
-
|
|
319
|
-
const next = ((decoder) => {
|
|
320
|
-
//* as default handler '/', say the current version.
|
|
321
|
-
if (MODE === 'LIST' && TYPE === '' && ID === '' && CMD === '') {
|
|
322
|
-
return () => __awaiter(this, void 0, void 0, function* () {
|
|
323
|
-
const $pack = (0, tools_1.loadJsonSync)('package.json');
|
|
324
|
-
const name = ($pack && $pack.name) || 'LEMON API';
|
|
325
|
-
const version = ($pack && $pack.version) || '0.0.0';
|
|
326
|
-
const modules = [`${name}/${version}`];
|
|
327
|
-
//* shows version of `lemon-core` via `dependencies`.
|
|
328
|
-
const coreVer = $pack && $pack.dependencies && $pack.dependencies['lemon-core'];
|
|
329
|
-
if (coreVer)
|
|
330
|
-
modules.push(`lemon-core/${coreVer.startsWith('^') ? coreVer.substring(1) : coreVer}`);
|
|
331
|
-
return modules.join('\n');
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
//* error if no decoder.
|
|
335
|
-
if (!decoder)
|
|
336
|
-
return null;
|
|
337
|
-
//* use decoder() to find target.
|
|
338
|
-
if (typeof decoder == 'function')
|
|
339
|
-
return decoder(MODE, ID, CMD, PATH);
|
|
340
|
-
else if (typeof decoder == 'object') {
|
|
341
|
-
const func = decoder.decode(MODE, ID, CMD, PATH);
|
|
342
|
-
if (!func)
|
|
343
|
-
return null; // avoid 'null' error.
|
|
344
|
-
const next = (i, p, b, c) => func.call(decoder, i, p, b, c);
|
|
345
|
-
return next;
|
|
346
|
-
}
|
|
318
|
+
//* error if no decoder.
|
|
319
|
+
if (!decoder)
|
|
347
320
|
return null;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
321
|
+
//* use decoder() to find target.
|
|
322
|
+
if (typeof decoder == 'function')
|
|
323
|
+
return decoder(MODE, ID, CMD, PATH);
|
|
324
|
+
else if (typeof decoder == 'object') {
|
|
325
|
+
const func = decoder.decode(MODE, ID, CMD, PATH);
|
|
326
|
+
if (!func)
|
|
327
|
+
return null; // avoid 'null' error.
|
|
328
|
+
const next = (i, p, b, c) => func.call(decoder, i, p, b, c);
|
|
329
|
+
return next;
|
|
353
330
|
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
331
|
+
return null;
|
|
332
|
+
})(this._handlers[TYPE]);
|
|
333
|
+
//* if no next, then report error.
|
|
334
|
+
if (!next || typeof next != 'function') {
|
|
335
|
+
(0, engine_1._err)(NS, `! WARN ! MISSING NEXT-HANDLER. event=`, engine_1.$U.json(event));
|
|
336
|
+
throw new Error(`404 NOT FOUND - ${MODE} /${TYPE}/${ID}${CMD ? `/${CMD}` : ''}`);
|
|
337
|
+
}
|
|
338
|
+
//* call next.. (it will return result or promised)
|
|
339
|
+
return (() => {
|
|
340
|
+
try {
|
|
341
|
+
const R = next(ID, $param, $body, context);
|
|
342
|
+
return R instanceof Promise ? R : Promise.resolve(R);
|
|
343
|
+
}
|
|
344
|
+
catch (e) {
|
|
345
|
+
return Promise.reject(e);
|
|
346
|
+
}
|
|
347
|
+
})();
|
|
365
348
|
}
|
|
349
|
+
/**
|
|
350
|
+
* builder of tools for http-headers
|
|
351
|
+
* - extracting header content, and parse.
|
|
352
|
+
*/
|
|
353
|
+
tools = (headers) => new MyHttpHeaderTool(headers);
|
|
366
354
|
/**
|
|
367
355
|
* pack the request context for Http request.
|
|
368
356
|
*
|
|
369
357
|
* @param event origin Event.
|
|
370
358
|
* @param orgContext (optional) original lambda.Context
|
|
371
359
|
*/
|
|
372
|
-
packContext(event, orgContext) {
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
return $ctx;
|
|
405
|
-
});
|
|
360
|
+
async packContext(event, orgContext) {
|
|
361
|
+
(0, engine_1._log)(NS, `packContext(${event ? '' : 'null'})..`);
|
|
362
|
+
if (!event)
|
|
363
|
+
return null;
|
|
364
|
+
//* prepare chain object.
|
|
365
|
+
const reqContext = event?.requestContext;
|
|
366
|
+
orgContext && (0, engine_1._log)(NS, `> orgContext =`, engine_1.$U.S(orgContext, 256, 32));
|
|
367
|
+
reqContext && (0, engine_1._log)(NS, `> reqContext =`, engine_1.$U.S(reqContext, 256, 32));
|
|
368
|
+
// STEP.1 support lambda call JWT Token authentication.
|
|
369
|
+
const headers = event.headers;
|
|
370
|
+
if (headers && headers[protocol_service_1.HEADER_PROTOCOL_CONTEXT]) {
|
|
371
|
+
//* if it is protocol request via lambda, then returns valid context.
|
|
372
|
+
const $param = protocol_1.default.service.asTransformer('web').transformToParam(event);
|
|
373
|
+
return $param?.context;
|
|
374
|
+
}
|
|
375
|
+
// STEP.2 use internal identity json data via python lambda call.
|
|
376
|
+
const $tool = this.tools(headers);
|
|
377
|
+
const identity = await $tool.parseIdentityHeader();
|
|
378
|
+
const _prepare = () => {
|
|
379
|
+
const cookie = $tool.parseCookiesHeader();
|
|
380
|
+
const domain = $tool.getHeader('host');
|
|
381
|
+
const referer = $tool.getHeader('referer');
|
|
382
|
+
const origin = $tool.getHeader('origin');
|
|
383
|
+
const userAgent = $tool.getHeader('user-agent');
|
|
384
|
+
const authorization = $tool.getHeader('authorization');
|
|
385
|
+
return { identity, cookie, domain, referer, origin, userAgent, authorization };
|
|
386
|
+
};
|
|
387
|
+
// STEP.3. prepare the final `next-context`.
|
|
388
|
+
const $ctx = await $tool.prepareContext(_prepare(), reqContext);
|
|
389
|
+
$ctx.source = protocol_1.default.service.myProtocolURI($ctx); // self service-uri as source
|
|
390
|
+
// FINIAL. returns
|
|
391
|
+
return $ctx;
|
|
406
392
|
}
|
|
407
393
|
}
|
|
408
394
|
exports.LambdaWEBHandler = LambdaWEBHandler;
|
|
409
|
-
//* shared config.
|
|
410
|
-
LambdaWEBHandler.REPORT_ERROR = lambda_handler_1.LambdaHandler.REPORT_ERROR;
|
|
411
395
|
/**
|
|
412
396
|
* class: `MyHttpHeaderTool`
|
|
413
397
|
* - basic implementation of HttpHeaderTool
|
|
414
398
|
*/
|
|
415
399
|
class MyHttpHeaderTool {
|
|
400
|
+
headers;
|
|
401
|
+
static _accountId;
|
|
402
|
+
static _accountIdPromise;
|
|
416
403
|
/**
|
|
417
404
|
* default constructor.
|
|
418
405
|
* @param headers
|
|
419
406
|
*/
|
|
420
407
|
constructor(headers, options) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
this.onlyDefined = test_helper_1.onlyDefined;
|
|
424
|
-
/**
|
|
425
|
-
* check if this request is from externals (like API-GW)
|
|
426
|
-
* @returns true if in external
|
|
427
|
-
*/
|
|
428
|
-
this.isExternal = () => {
|
|
429
|
-
const host = this.getHeader('host');
|
|
430
|
-
const isExternal = host ? true : false;
|
|
431
|
-
return !!isExternal;
|
|
432
|
-
};
|
|
433
|
-
const isClone = (_a = options === null || options === void 0 ? void 0 : options.isClone) !== null && _a !== void 0 ? _a : true;
|
|
434
|
-
this.headers = isClone ? Object.assign({}, headers) : headers;
|
|
408
|
+
const isClone = options?.isClone ?? true;
|
|
409
|
+
this.headers = isClone ? { ...headers } : headers;
|
|
435
410
|
}
|
|
436
411
|
hello() {
|
|
437
412
|
return 'header-tool-by-default';
|
|
438
413
|
}
|
|
414
|
+
/** expose `onlyDefined` */
|
|
415
|
+
onlyDefined = test_helper_1.onlyDefined;
|
|
416
|
+
/**
|
|
417
|
+
* build default JWT secret from AWS account id + magic key.
|
|
418
|
+
*/
|
|
419
|
+
buildJwtSecret = async () => {
|
|
420
|
+
const accountId = await this.getAccountId();
|
|
421
|
+
const secret = JWT_MAGIC_KEY; // 유출되면 피곤함.
|
|
422
|
+
const _hmac = (msg, key = secret) => engine_1.$U.hmac(msg, key, 'sha256', 'base64'); // 기본 로직
|
|
423
|
+
return _hmac(_hmac(accountId, _hmac(JWT_SIGN_KEY)), JWT_SIGN_KEY);
|
|
424
|
+
};
|
|
425
|
+
/**
|
|
426
|
+
* get current aws account-id via STS. (once)
|
|
427
|
+
*/
|
|
428
|
+
getAccountId = async () => {
|
|
429
|
+
if (MyHttpHeaderTool._accountId)
|
|
430
|
+
return MyHttpHeaderTool._accountId;
|
|
431
|
+
if (!MyHttpHeaderTool._accountIdPromise) {
|
|
432
|
+
MyHttpHeaderTool._accountIdPromise = (async () => {
|
|
433
|
+
// 1. getHelloArn()에서 ARN을 통해 accountId 추출 시도
|
|
434
|
+
try {
|
|
435
|
+
const arn = (0, engine_1.getHelloArn)(null, NS);
|
|
436
|
+
if (arn && arn.startsWith('arn:aws:')) {
|
|
437
|
+
const accountId = arn.split(':')[4];
|
|
438
|
+
if (accountId)
|
|
439
|
+
return accountId;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
catch (e) {
|
|
443
|
+
(0, engine_1._log)(NS, '! getHelloArn failed, fallback to STS');
|
|
444
|
+
}
|
|
445
|
+
// 2. fallback: STS 호출 (awsConfig 사용)
|
|
446
|
+
const cfg = (0, tools_1.awsConfig)(engine_1.$engine);
|
|
447
|
+
const sts = new client_sts_1.STSClient(cfg);
|
|
448
|
+
const data = await sts.send(new client_sts_1.GetCallerIdentityCommand({}));
|
|
449
|
+
return data?.Account || '000000000000';
|
|
450
|
+
})().catch(err => {
|
|
451
|
+
(0, engine_1._err)(NS, '! err@jwt.accountId =', err);
|
|
452
|
+
return '000000000000';
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
MyHttpHeaderTool._accountId = await MyHttpHeaderTool._accountIdPromise;
|
|
456
|
+
return MyHttpHeaderTool._accountId;
|
|
457
|
+
};
|
|
439
458
|
/**
|
|
440
459
|
* get values by name
|
|
441
460
|
* @param name case-insentive name of field
|
|
@@ -470,6 +489,15 @@ class MyHttpHeaderTool {
|
|
|
470
489
|
const vals = this.getHeaders(name);
|
|
471
490
|
return vals.length < 1 ? undefined : vals[vals.length - 1];
|
|
472
491
|
}
|
|
492
|
+
/**
|
|
493
|
+
* check if this request is from externals (like API-GW)
|
|
494
|
+
* @returns true if in external
|
|
495
|
+
*/
|
|
496
|
+
isExternal = () => {
|
|
497
|
+
const host = this.getHeader('host');
|
|
498
|
+
const isExternal = host ? true : false;
|
|
499
|
+
return !!isExternal;
|
|
500
|
+
};
|
|
473
501
|
/**
|
|
474
502
|
* parse of header[`x-lemon-identity`] to get the instance of `NextIdentity`
|
|
475
503
|
* - lambda 호출의 2가지 방법이 있음 (interval vs external)
|
|
@@ -484,48 +512,43 @@ class MyHttpHeaderTool {
|
|
|
484
512
|
* - support ONLY JWT Token authentication (verification).
|
|
485
513
|
* - iat
|
|
486
514
|
*/
|
|
487
|
-
parseIdentityHeader(name = HEADER_LEMON_IDENTITY) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
if (!val) {
|
|
496
|
-
//NOP
|
|
497
|
-
}
|
|
498
|
-
else if (isInternal && val.startsWith('{') && val.endsWith('}')) {
|
|
499
|
-
result = yield this.parseIdentityJson(val);
|
|
500
|
-
}
|
|
501
|
-
else if (typeof val === 'string' && val.split('.').length === 3) {
|
|
502
|
-
result = yield this.parseIdentityJWT(val);
|
|
503
|
-
}
|
|
515
|
+
async parseIdentityHeader(name = HEADER_LEMON_IDENTITY) {
|
|
516
|
+
//* internal means `request from internal services`
|
|
517
|
+
const isInternal = !this.isExternal();
|
|
518
|
+
const val = this.getHeader(name);
|
|
519
|
+
let result = val ? { meta: val } : {};
|
|
520
|
+
try {
|
|
521
|
+
if (!val) {
|
|
522
|
+
//NOP
|
|
504
523
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
(0, engine_1._err)(NS, '!WARN! identity =', val);
|
|
508
|
-
result.error = (0, test_helper_1.GETERR)(e);
|
|
524
|
+
else if (isInternal && val.startsWith('{') && val.endsWith('}')) {
|
|
525
|
+
result = await this.parseIdentityJson(val);
|
|
509
526
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
527
|
+
else if (typeof val === 'string' && val.split('.').length === 3) {
|
|
528
|
+
result = await this.parseIdentityJWT(val);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
catch (e) {
|
|
532
|
+
(0, engine_1._err)(NS, '!WARN! parse.err =', e);
|
|
533
|
+
(0, engine_1._err)(NS, '!WARN! identity =', val);
|
|
534
|
+
result.error = (0, test_helper_1.GETERR)(e);
|
|
535
|
+
}
|
|
536
|
+
//* overwrite finally language selection.
|
|
537
|
+
const lang = this.parseLanguageHeader() ?? result?.lang;
|
|
538
|
+
return { ...result, lang };
|
|
514
539
|
}
|
|
515
540
|
/**
|
|
516
541
|
* parse as identity from json encoded text.
|
|
517
542
|
*/
|
|
518
|
-
parseIdentityJson(val) {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
return data;
|
|
528
|
-
});
|
|
543
|
+
async parseIdentityJson(val) {
|
|
544
|
+
if (typeof val !== 'string')
|
|
545
|
+
throw new Error(`@val[${val}] is invalid - not string!`);
|
|
546
|
+
//* (ONLY for internal) parse payload as `json`
|
|
547
|
+
const data = JSON.parse(val);
|
|
548
|
+
// if (typeof data?.ns !== 'string') throw new Error(`.ns[${data?.ns}] is required - IdentityHeader`);
|
|
549
|
+
if (typeof data?.sid !== 'string' || !data?.sid)
|
|
550
|
+
throw new Error(`.sid[${data?.sid}] is required - IdentityHeader`);
|
|
551
|
+
return data;
|
|
529
552
|
}
|
|
530
553
|
/**
|
|
531
554
|
* find(or make) the proper KMSService per key
|
|
@@ -542,87 +565,85 @@ class MyHttpHeaderTool {
|
|
|
542
565
|
/**
|
|
543
566
|
* encode as JWT string.
|
|
544
567
|
*/
|
|
545
|
-
encodeIdentityJWT(identity, params) {
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
568
|
+
async encodeIdentityJWT(identity, params) {
|
|
569
|
+
// STEP.0 validate paramters.
|
|
570
|
+
if (!identity || typeof identity !== 'object')
|
|
571
|
+
throw new Error(`@identity (object) is required - but ${typeof identity}`);
|
|
572
|
+
// STEP.1 prepare payload data
|
|
573
|
+
const current = params?.current ?? engine_1.$U.current_time_ms();
|
|
574
|
+
const alias = params?.alias;
|
|
575
|
+
const useHs256 = params?.useHs256 ?? false;
|
|
576
|
+
const service = config_1.default?.config?.getService?.();
|
|
577
|
+
// service 검증 (useHs256 + alias 없을 때만)
|
|
578
|
+
if (useHs256 && !alias && service && !/^[a-z][a-zA-Z0-9-]*$/.test(service)) {
|
|
579
|
+
throw new Error(`@service[${service}] is invalid - encodeIdentityJWT()`);
|
|
580
|
+
}
|
|
581
|
+
// issuer 결정
|
|
582
|
+
const iss = alias ? `kms/${alias}` : useHs256 ? `api/${service}` : null;
|
|
583
|
+
// alg 헤더 결정
|
|
584
|
+
const alg = alias ? 'RS256' : useHs256 ? 'HS256' : 'RS256';
|
|
585
|
+
const payload = {
|
|
586
|
+
...identity,
|
|
587
|
+
iss, //* issuer name.
|
|
588
|
+
iat: Math.floor(current / 1000), //* issued at
|
|
589
|
+
exp: params?.exp ?? Math.floor(current / 1000) + 24 * 60 * 60, //* max 1 day.
|
|
590
|
+
};
|
|
591
|
+
const base64url = (t) => (0, aws_kms_service_1.fromBase64)(Buffer.from(t).toString('base64'));
|
|
592
|
+
const data = {
|
|
593
|
+
header: base64url(JSON.stringify({ alg, typ: 'JWT' })),
|
|
594
|
+
payload: base64url(JSON.stringify(payload)),
|
|
595
|
+
};
|
|
596
|
+
// STEP.2 encode and calc signature.
|
|
597
|
+
const message = [data.header, data.payload].join('.');
|
|
598
|
+
const signature = alias || useHs256 ? await this.signToken(alias, message) : '';
|
|
599
|
+
const token = [message, signature].join('.');
|
|
600
|
+
return { signature, message, token };
|
|
570
601
|
}
|
|
571
602
|
/**
|
|
572
603
|
* parse as jwt-token, and validate the signature.
|
|
604
|
+
* - supports both `kms/*` and `api/*` issuers via verifyToken()
|
|
573
605
|
*/
|
|
574
|
-
parseIdentityJWT(token, params) {
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
return data;
|
|
605
|
-
}
|
|
606
|
-
else if (typeof iss === 'string' && iss.startsWith('kms/')) {
|
|
607
|
-
const alias = _alias(iss);
|
|
608
|
-
const $kms = alias ? this.findKMSService(`alias/${alias}`) : null;
|
|
609
|
-
const message = [header, payload].join('.');
|
|
610
|
-
const verified = $kms
|
|
611
|
-
? yield $kms.verify(message, signature, { throwable: true }).catch(e => {
|
|
612
|
-
throw new Error(`@signature[] is invalid (kms: ${(0, test_helper_1.GETERR)(e)}) - ${errScope}`);
|
|
613
|
-
})
|
|
614
|
-
: false;
|
|
615
|
-
if (!verified)
|
|
616
|
-
throw new Error(`@signature[] is invalid (failed to verify by iss:${iss}) - ${errScope}`);
|
|
617
|
-
if (!exp)
|
|
618
|
-
throw new Error(`.exp[${exp}] is invalid (empty) - ${errScope}`);
|
|
619
|
-
if (exp * 1000 < current)
|
|
620
|
-
throw new Error(`.exp[${engine_1.$U.ts(exp * 1000)}] is invalid (expired) - ${errScope}`);
|
|
621
|
-
return data;
|
|
622
|
-
}
|
|
623
|
-
//* or throw
|
|
624
|
-
throw new Error(`@iss[${iss}] is invalid (unsupportable issuer) - ${errScope}`);
|
|
606
|
+
async parseIdentityJWT(token, params) {
|
|
607
|
+
const isVerify = params?.verify ?? true;
|
|
608
|
+
const errScope = `verifyJWT(http)`;
|
|
609
|
+
//* it must be JWT Token. verify signature, and load.
|
|
610
|
+
if (typeof token !== 'string' || !token)
|
|
611
|
+
throw new Error(`@token (string) is required (but ${typeof token}) - ${errScope}`);
|
|
612
|
+
const sections = token.split('.');
|
|
613
|
+
if (sections.length !== 3)
|
|
614
|
+
throw new Error(`@token[${token}] is invalid (format) - ${errScope}`);
|
|
615
|
+
// STEP.1 decode jwt, and extract { iss, iat, exp }
|
|
616
|
+
const current = params?.current ?? engine_1.$U.current_time_ms();
|
|
617
|
+
const [header, payload, signature] = sections;
|
|
618
|
+
const message = [header, payload].join('.');
|
|
619
|
+
const data = engine_1.$U.jwt().decode(token, { complete: false, json: true });
|
|
620
|
+
if (!data)
|
|
621
|
+
throw new Error(`@token[${token}] is invalid (failed to decode) - ${errScope}`);
|
|
622
|
+
const { iss, iat, exp } = data;
|
|
623
|
+
// STEP.1-1 validate parameters.
|
|
624
|
+
if (typeof iss !== 'string' && iss !== null)
|
|
625
|
+
throw new Error(`.iss (string) is required - ${errScope}`);
|
|
626
|
+
if (typeof iat !== 'number' && iat !== null)
|
|
627
|
+
throw new Error(`.iat (number) is required - ${errScope}`);
|
|
628
|
+
if (typeof exp !== 'number' && exp !== null)
|
|
629
|
+
throw new Error(`.exp (number) is required - ${errScope}`);
|
|
630
|
+
//* skip verification if not required
|
|
631
|
+
if (!isVerify)
|
|
632
|
+
return data;
|
|
633
|
+
// STEP.2 verify signature by iss (supports kms/* and api/*)
|
|
634
|
+
const verified = await this.verifyToken(iss, message, signature, { token }).catch(e => {
|
|
635
|
+
throw new Error(`@signature[] is invalid (${(0, test_helper_1.GETERR)(e)}) - ${errScope}`);
|
|
625
636
|
});
|
|
637
|
+
if (!verified?.valid) {
|
|
638
|
+
const errMsg = verified?.error || `failed to verify by ${iss}`;
|
|
639
|
+
throw new Error(`@signature[] is invalid (${errMsg}) - ${errScope}`);
|
|
640
|
+
}
|
|
641
|
+
// STEP.3 validate expiration
|
|
642
|
+
if (!exp)
|
|
643
|
+
throw new Error(`.exp[${exp}] is invalid (empty) - ${errScope}`);
|
|
644
|
+
if (exp * 1000 < current)
|
|
645
|
+
throw new Error(`.exp[${engine_1.$U.ts(exp * 1000)}] is invalid (expired) - ${errScope}`);
|
|
646
|
+
return data;
|
|
626
647
|
}
|
|
627
648
|
/**
|
|
628
649
|
* parse of header[HEADER_LEMON_LANGUAGE] to get language-type.
|
|
@@ -650,202 +671,93 @@ class MyHttpHeaderTool {
|
|
|
650
671
|
/**
|
|
651
672
|
* override with AWS request-context
|
|
652
673
|
*/
|
|
653
|
-
prepareContext($org, reqContext) {
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
if (
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
$idt.
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const context = Object.assign(Object.assign({}, $org), { userAgent, clientIp, requestId, accountId, domain });
|
|
685
|
-
return context;
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
exports.MyHttpHeaderTool = MyHttpHeaderTool;
|
|
690
|
-
class MyHttpHeaderToolV2 extends MyHttpHeaderTool {
|
|
691
|
-
constructor(headers, reqContext) {
|
|
692
|
-
super(headers);
|
|
693
|
-
/**
|
|
694
|
-
* build default JWT secret from AWS account id + magic key.
|
|
695
|
-
*/
|
|
696
|
-
this.buildJwtSecret = () => {
|
|
697
|
-
var _a, _b;
|
|
698
|
-
const accountId = (_b = (_a = this.reqContext) === null || _a === void 0 ? void 0 : _a.accountId) !== null && _b !== void 0 ? _b : '000000000000';
|
|
699
|
-
const secret = JWT_MAGIC_KEY; // 유출되면 피곤함.
|
|
700
|
-
const _hmac = (msg, key = secret) => engine_1.$U.hmac(msg, key, 'sha256', 'base64'); // 기본 로직
|
|
701
|
-
return _hmac(_hmac(accountId, _hmac(JWT_SIGN_KEY)), JWT_SIGN_KEY);
|
|
702
|
-
};
|
|
703
|
-
this.reqContext = reqContext;
|
|
704
|
-
}
|
|
705
|
-
hello() {
|
|
706
|
-
return 'header-tool-v2';
|
|
707
|
-
}
|
|
708
|
-
/**
|
|
709
|
-
* encode as JWT string.
|
|
710
|
-
*/
|
|
711
|
-
encodeIdentityJWT(identity, params) {
|
|
712
|
-
var _a, _b, _c, _d;
|
|
713
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
714
|
-
// STEP.0 validate paramters.
|
|
715
|
-
if (!identity || typeof identity !== 'object')
|
|
716
|
-
throw new Error(`@identity (object) is required - but ${typeof identity}`);
|
|
717
|
-
// STEP.1 prepare payload data
|
|
718
|
-
const current = (_a = params === null || params === void 0 ? void 0 : params.current) !== null && _a !== void 0 ? _a : engine_1.$U.current_time_ms();
|
|
719
|
-
const alias = params === null || params === void 0 ? void 0 : params.alias;
|
|
720
|
-
const useKms = !!(params === null || params === void 0 ? void 0 : params.alias);
|
|
721
|
-
const service = (_c = (_b = config_1.default === null || config_1.default === void 0 ? void 0 : config_1.default.config) === null || _b === void 0 ? void 0 : _b.getService) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
722
|
-
// validate service format
|
|
723
|
-
if (!useKms && service && !/^[a-z][a-zA-Z0-9-]*$/.test(service)) {
|
|
724
|
-
throw new Error(`@service[${service}] is invalid - encodeIdentityJWT()`);
|
|
725
|
-
}
|
|
726
|
-
const payload = Object.assign(Object.assign({}, identity), { iss: useKms ? `kms/${alias}` : `api/${service}`, iat: Math.floor(current / 1000), exp: (_d = params === null || params === void 0 ? void 0 : params.exp) !== null && _d !== void 0 ? _d : Math.floor(current / 1000) + 24 * 60 * 60 });
|
|
727
|
-
const base64url = (t) => (0, aws_kms_service_1.fromBase64)(Buffer.from(t).toString('base64'));
|
|
728
|
-
const alg = useKms ? 'RS256' : 'HS256';
|
|
729
|
-
const data = {
|
|
730
|
-
header: base64url(JSON.stringify({
|
|
731
|
-
alg,
|
|
732
|
-
typ: 'JWT',
|
|
733
|
-
})),
|
|
734
|
-
payload: base64url(JSON.stringify(payload)),
|
|
735
|
-
};
|
|
736
|
-
// STEP.2 encode and calc signature.
|
|
737
|
-
const message = [data.header, data.payload].join('.');
|
|
738
|
-
const signature = yield this.signToken(alias, message);
|
|
739
|
-
const token = [message, signature].join('.');
|
|
740
|
-
return { signature, message, token };
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* parse as jwt-token, and validate the signature.
|
|
745
|
-
*/
|
|
746
|
-
parseIdentityJWT(token, params) {
|
|
747
|
-
var _a, _b;
|
|
748
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
749
|
-
const isVerify = (_a = params === null || params === void 0 ? void 0 : params.verify) !== null && _a !== void 0 ? _a : true;
|
|
750
|
-
const errScope = `verifyJWT(http)`;
|
|
751
|
-
//* it must be JWT Token. verify signature, and load.
|
|
752
|
-
if (typeof token !== 'string' || !token)
|
|
753
|
-
throw new Error(`@token (string) is required (but ${typeof token}) - ${errScope}`);
|
|
754
|
-
const sections = token.split('.');
|
|
755
|
-
if (sections.length !== 3)
|
|
756
|
-
throw new Error(`@token[${token}] is invalid (format) - ${errScope}`);
|
|
757
|
-
// STEP.1 decode jwt, and extract { iss, iat, exp }
|
|
758
|
-
const current = (_b = params === null || params === void 0 ? void 0 : params.current) !== null && _b !== void 0 ? _b : engine_1.$U.current_time_ms();
|
|
759
|
-
const [header, payload, signature] = sections;
|
|
760
|
-
const message = [header, payload].join('.');
|
|
761
|
-
const data = engine_1.$U.jwt().decode(token, { complete: false, json: true });
|
|
762
|
-
if (!data)
|
|
763
|
-
throw new Error(`@token[${token}] is invalid (failed to decode) - ${errScope}`);
|
|
764
|
-
const { iss, iat, exp } = data;
|
|
765
|
-
// STEP.1-1 validate parameters.
|
|
766
|
-
if (typeof iss !== 'string' && iss !== null)
|
|
767
|
-
throw new Error(`.iss (string) is required - ${errScope}`);
|
|
768
|
-
if (typeof iat !== 'number' && iat !== null)
|
|
769
|
-
throw new Error(`.iat (number) is required - ${errScope}`);
|
|
770
|
-
if (typeof exp !== 'number' && exp !== null)
|
|
771
|
-
throw new Error(`.exp (number) is required - ${errScope}`);
|
|
772
|
-
//* skip verification if not required
|
|
773
|
-
if (!isVerify)
|
|
774
|
-
return data;
|
|
775
|
-
// STEP.2 verify signature by iss
|
|
776
|
-
const verified = yield this.verifyToken(iss, message, signature, { token }).catch(e => {
|
|
777
|
-
throw new Error(`@signature[] is invalid (${(0, test_helper_1.GETERR)(e)}) - ${errScope}`);
|
|
778
|
-
});
|
|
779
|
-
if (!(verified === null || verified === void 0 ? void 0 : verified.valid)) {
|
|
780
|
-
const errMsg = (verified === null || verified === void 0 ? void 0 : verified.error) || `failed to verify by ${iss}`;
|
|
781
|
-
throw new Error(`@signature[] is invalid (${errMsg}) - ${errScope}`);
|
|
782
|
-
}
|
|
783
|
-
// STEP.3 validate expiration
|
|
784
|
-
if (!exp)
|
|
785
|
-
throw new Error(`.exp[${exp}] is invalid (empty) - ${errScope}`);
|
|
786
|
-
if (exp * 1000 < current)
|
|
787
|
-
throw new Error(`.exp[${engine_1.$U.ts(exp * 1000)}] is invalid (expired) - ${errScope}`);
|
|
788
|
-
return data;
|
|
789
|
-
});
|
|
674
|
+
async prepareContext($org, reqContext) {
|
|
675
|
+
const errScope = `web.prepareContext(${$org?.requestId ?? ''})`;
|
|
676
|
+
// STEP.4 override w/ cognito authentication to NextIdentity.
|
|
677
|
+
if (reqContext?.identity?.cognitoIdentityPoolId !== undefined) {
|
|
678
|
+
const $idt = $org?.identity;
|
|
679
|
+
if (!$idt)
|
|
680
|
+
throw new Error(`.identity (NextIdentity) is required - ${errScope}`);
|
|
681
|
+
const $req = reqContext.identity;
|
|
682
|
+
(0, engine_1._inf)(NS, '! identity(req) :=', engine_1.$U.json($req));
|
|
683
|
+
$idt.identityProvider = $req.cognitoAuthenticationProvider; // provider string.
|
|
684
|
+
$idt.identityPoolId = $req.cognitoIdentityPoolId; // identity-pool-id like 'ap-northeast-2:618ce9d2-3ad6-49df-b3b3-e248ea51425e'
|
|
685
|
+
$idt.identityId = $req.cognitoIdentityId; // identity-id like 'ap-northeast-2:dbd95fb4-7423-48b8-8a04-56e5bc95e444'
|
|
686
|
+
$idt.accountId = $req.accountId; // account-id should be same as context.accountId
|
|
687
|
+
$idt.userAgent = $req.userAgent; // user-agent string.
|
|
688
|
+
if (typeof $req.caller == 'string')
|
|
689
|
+
$idt.caller = $req.caller;
|
|
690
|
+
if (typeof $req.accessKey == 'string')
|
|
691
|
+
$idt.accessKey = $req.accessKey;
|
|
692
|
+
if (typeof $req.apiKey == 'string')
|
|
693
|
+
$idt.apiKey = $req.apiKey;
|
|
694
|
+
(0, engine_1._inf)(NS, '! identity(new) :=', engine_1.$U.json({ ...$idt }));
|
|
695
|
+
}
|
|
696
|
+
// STEP.5 extract additional request infor from req-context.
|
|
697
|
+
const clientIp = `${reqContext?.identity?.sourceIp || $org?.clientIp || ''}`;
|
|
698
|
+
const userAgent = `${reqContext?.identity?.userAgent || $org?.userAgent || ''}`;
|
|
699
|
+
const requestId = `${reqContext?.requestId || $org?.requestId || ''}`;
|
|
700
|
+
const accountId = `${reqContext?.accountId || $org?.accountId || ''}`;
|
|
701
|
+
const domain = `${reqContext?.domainName || $org?.domain || ''}`; //* chore avoid null of headers
|
|
702
|
+
//* save into headers and returns.
|
|
703
|
+
const context = { ...$org, userAgent, clientIp, requestId, accountId, domain };
|
|
704
|
+
return context;
|
|
790
705
|
}
|
|
791
706
|
/**
|
|
792
707
|
* verify token signature by iss.
|
|
793
708
|
* - `kms/*`: verify by KMS (RS256)
|
|
794
709
|
* - `api/*`: verify by HS256 (local) or protocol (remote)
|
|
795
710
|
*/
|
|
796
|
-
verifyToken(iss, message, signature, options) {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
const
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
711
|
+
async verifyToken(iss, message, signature, options) {
|
|
712
|
+
const errScope = `verifyToken(${iss ?? ''})`;
|
|
713
|
+
const _alias = (iss, prefix) => {
|
|
714
|
+
const value = iss.substring(prefix.length);
|
|
715
|
+
return value.includes(',') ? value.substring(0, value.indexOf(',')) : value;
|
|
716
|
+
};
|
|
717
|
+
try {
|
|
718
|
+
// KMS verification (RS256) - iss starts with 'kms/'
|
|
719
|
+
if (typeof iss === 'string' && iss.startsWith('kms/')) {
|
|
720
|
+
const alias = _alias(iss, 'kms/');
|
|
721
|
+
if (!alias)
|
|
722
|
+
throw new Error(`@alias (string) is required - ${errScope}`);
|
|
723
|
+
const $kms = this.findKMSService(`alias/${alias}`);
|
|
724
|
+
const valid = await $kms.verify(message, signature, { throwable: true });
|
|
725
|
+
return { valid };
|
|
726
|
+
}
|
|
727
|
+
// API verification - iss starts with 'api/'
|
|
728
|
+
if (typeof iss === 'string' && iss.startsWith('api/')) {
|
|
729
|
+
const issuer = iss.substring(4);
|
|
730
|
+
// validate issuer format
|
|
731
|
+
if (!/^[a-z][a-zA-Z0-9-]*$/.test(issuer)) {
|
|
732
|
+
throw new Error(`@issuer[${issuer}] is invalid - ${errScope}`);
|
|
813
733
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
}
|
|
821
|
-
const currentService = (_b = (_a = config_1.default === null || config_1.default === void 0 ? void 0 : config_1.default.config) === null || _a === void 0 ? void 0 : _a.getService) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
822
|
-
// verify if issued by current service
|
|
823
|
-
if (issuer === currentService) {
|
|
824
|
-
const secret = this.buildJwtSecret();
|
|
825
|
-
const expected = (0, aws_kms_service_1.fromBase64)(engine_1.$U.hmac(message, secret, 'sha256', 'base64'));
|
|
826
|
-
const valid = expected === signature;
|
|
827
|
-
return { valid };
|
|
828
|
-
}
|
|
829
|
-
// verify via protocol
|
|
830
|
-
const token = (_c = options === null || options === void 0 ? void 0 : options.token) !== null && _c !== void 0 ? _c : `${message}.${signature}`;
|
|
831
|
-
const body = { token };
|
|
832
|
-
const payload = {
|
|
833
|
-
service: issuer,
|
|
834
|
-
type: 'oauth',
|
|
835
|
-
mode: 'POST',
|
|
836
|
-
id: 'verify-token',
|
|
837
|
-
body,
|
|
838
|
-
context: {},
|
|
839
|
-
};
|
|
840
|
-
const res = yield protocol_1.default.service.execute(payload);
|
|
841
|
-
return res;
|
|
734
|
+
const currentService = config_1.default?.config?.getService?.();
|
|
735
|
+
// verify if issued by current service
|
|
736
|
+
if (issuer === currentService) {
|
|
737
|
+
const secret = await this.buildJwtSecret();
|
|
738
|
+
const expected = (0, aws_kms_service_1.fromBase64)(engine_1.$U.hmac(message, secret, 'sha256', 'base64'));
|
|
739
|
+
const valid = expected === signature;
|
|
740
|
+
return { valid };
|
|
842
741
|
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
742
|
+
// verify via protocol
|
|
743
|
+
const token = options?.token ?? `${message}.${signature}`;
|
|
744
|
+
const body = { token };
|
|
745
|
+
const payload = {
|
|
746
|
+
service: issuer,
|
|
747
|
+
type: 'oauth',
|
|
748
|
+
mode: 'POST',
|
|
749
|
+
id: 'verify-token',
|
|
750
|
+
body,
|
|
751
|
+
context: {},
|
|
752
|
+
};
|
|
753
|
+
const res = await protocol_1.default.service.execute(payload);
|
|
754
|
+
return res;
|
|
847
755
|
}
|
|
848
|
-
|
|
756
|
+
throw new Error(`@iss[${iss}] is invalid (unsupportable issuer) - ${errScope}`);
|
|
757
|
+
}
|
|
758
|
+
catch (e) {
|
|
759
|
+
return { valid: false, error: (0, test_helper_1.GETERR)(e) };
|
|
760
|
+
}
|
|
849
761
|
}
|
|
850
762
|
/**
|
|
851
763
|
* sign the message w/ alias
|
|
@@ -854,30 +766,28 @@ class MyHttpHeaderToolV2 extends MyHttpHeaderTool {
|
|
|
854
766
|
* @param alias kms key alias
|
|
855
767
|
* @param message the jwt message string
|
|
856
768
|
*/
|
|
857
|
-
signToken(alias, message) {
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
});
|
|
769
|
+
async signToken(alias, message) {
|
|
770
|
+
const errScope = `signToken(${alias ?? ''})`;
|
|
771
|
+
if (!message)
|
|
772
|
+
throw new Error(`@message (string) is required - ${errScope}`);
|
|
773
|
+
const useKms = !!alias;
|
|
774
|
+
if (useKms) {
|
|
775
|
+
// KMS verification (RS256)
|
|
776
|
+
if (!alias)
|
|
777
|
+
return '';
|
|
778
|
+
const $kms = new aws_kms_service_1.AWSKMSService(`alias/${alias}`);
|
|
779
|
+
const signature = await $kms.sign(message, true);
|
|
780
|
+
(0, engine_1._inf)(NS, `> signature[${alias}] with KMS =`, signature);
|
|
781
|
+
return signature;
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
// HS256: sign directly with secret
|
|
785
|
+
const secret = await this.buildJwtSecret();
|
|
786
|
+
const signature = (0, aws_kms_service_1.fromBase64)(engine_1.$U.hmac(message, secret, 'sha256', 'base64'));
|
|
787
|
+
(0, engine_1._inf)(NS, `> signature[${alias}] with HS256 =`, signature);
|
|
788
|
+
return signature;
|
|
789
|
+
}
|
|
880
790
|
}
|
|
881
791
|
}
|
|
882
|
-
exports.
|
|
792
|
+
exports.MyHttpHeaderTool = MyHttpHeaderTool;
|
|
883
793
|
//# sourceMappingURL=lambda-web-handler.js.map
|