aws-service-stack 0.18.447 → 0.18.450

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.
Files changed (76) hide show
  1. package/README.md +79 -7
  2. package/dist/controller/base-controller.d.ts +2 -1
  3. package/dist/controller/base-controller.d.ts.map +1 -1
  4. package/dist/controller/base-controller.js +64 -15
  5. package/dist/controller/base-controller.js.map +1 -1
  6. package/dist/controller/controller-api.d.ts +13 -2
  7. package/dist/controller/controller-api.d.ts.map +1 -1
  8. package/dist/controller/controller-api.js +77 -10
  9. package/dist/controller/controller-api.js.map +1 -1
  10. package/dist/controller/controller-request.d.ts +3 -0
  11. package/dist/controller/controller-request.d.ts.map +1 -1
  12. package/dist/controller/controller-request.js +5 -0
  13. package/dist/controller/controller-request.js.map +1 -1
  14. package/dist/controller/controller-role.d.ts +2 -1
  15. package/dist/controller/controller-role.d.ts.map +1 -1
  16. package/dist/controller/controller-role.js +12 -3
  17. package/dist/controller/controller-role.js.map +1 -1
  18. package/dist/exception/exception-handler.d.ts.map +1 -1
  19. package/dist/exception/exception-handler.js +17 -2
  20. package/dist/exception/exception-handler.js.map +1 -1
  21. package/dist/model/base.config.d.ts +4 -1
  22. package/dist/model/base.config.d.ts.map +1 -1
  23. package/dist/model/base.config.js +1 -0
  24. package/dist/model/base.config.js.map +1 -1
  25. package/dist/model/base.model.d.ts +3 -0
  26. package/dist/model/base.model.d.ts.map +1 -1
  27. package/dist/model/base.model.js.map +1 -1
  28. package/dist/model/caller.model.d.ts +22 -0
  29. package/dist/model/caller.model.d.ts.map +1 -0
  30. package/dist/model/caller.model.js +12 -0
  31. package/dist/model/caller.model.js.map +1 -0
  32. package/dist/model/index.d.ts +1 -0
  33. package/dist/model/index.d.ts.map +1 -1
  34. package/dist/model/index.js +1 -0
  35. package/dist/model/index.js.map +1 -1
  36. package/dist/model/validation.model.d.ts +15 -1
  37. package/dist/model/validation.model.d.ts.map +1 -1
  38. package/dist/model/validation.model.js +3 -1
  39. package/dist/model/validation.model.js.map +1 -1
  40. package/dist/repositories/base-es.repo.d.ts.map +1 -1
  41. package/dist/repositories/base-es.repo.js +4 -0
  42. package/dist/repositories/base-es.repo.js.map +1 -1
  43. package/dist/utils/auth/caller.d.ts +9 -0
  44. package/dist/utils/auth/caller.d.ts.map +1 -0
  45. package/dist/utils/auth/caller.js +108 -0
  46. package/dist/utils/auth/caller.js.map +1 -0
  47. package/dist/utils/auth/policy-view.d.ts +42 -0
  48. package/dist/utils/auth/policy-view.d.ts.map +1 -0
  49. package/dist/utils/auth/policy-view.js +170 -0
  50. package/dist/utils/auth/policy-view.js.map +1 -0
  51. package/dist/utils/http/api-event.d.ts +23 -0
  52. package/dist/utils/http/api-event.d.ts.map +1 -0
  53. package/dist/utils/http/api-event.js +84 -0
  54. package/dist/utils/http/api-event.js.map +1 -0
  55. package/dist/utils/http/http.util.d.ts +12 -2
  56. package/dist/utils/http/http.util.d.ts.map +1 -1
  57. package/dist/utils/http/http.util.js +50 -24
  58. package/dist/utils/http/http.util.js.map +1 -1
  59. package/dist/utils/index.d.ts +4 -0
  60. package/dist/utils/index.d.ts.map +1 -1
  61. package/dist/utils/index.js +4 -0
  62. package/dist/utils/index.js.map +1 -1
  63. package/dist/utils/opensearch/opensearch.mapping.d.ts +35 -0
  64. package/dist/utils/opensearch/opensearch.mapping.d.ts.map +1 -0
  65. package/dist/utils/opensearch/opensearch.mapping.js +95 -0
  66. package/dist/utils/opensearch/opensearch.mapping.js.map +1 -0
  67. package/dist/utils/opensearch/opensearch.parser.aggs.d.ts +2 -1
  68. package/dist/utils/opensearch/opensearch.parser.aggs.d.ts.map +1 -1
  69. package/dist/utils/opensearch/opensearch.parser.aggs.js +38 -4
  70. package/dist/utils/opensearch/opensearch.parser.aggs.js.map +1 -1
  71. package/dist/utils/opensearch/opensearch.parser.d.ts +1 -1
  72. package/dist/utils/reflection.util.d.ts +4 -0
  73. package/dist/utils/reflection.util.d.ts.map +1 -1
  74. package/dist/utils/reflection.util.js +17 -0
  75. package/dist/utils/reflection.util.js.map +1 -1
  76. package/package.json +76 -19
package/README.md CHANGED
@@ -46,15 +46,21 @@ npm install aws-service-stack
46
46
 
47
47
  ### Peer Dependencies
48
48
 
49
- This package requires the following AWS SDK v3 packages as peer dependencies:
49
+ Backend services must install the peer dependencies below. They are marked optional in
50
+ `peerDependenciesMeta`, so a project that lists the package without importing it at runtime
51
+ does not get unmet-peer warnings. Importing the package without them fails at runtime, not at install time.
50
52
 
51
53
  ```bash
52
- npm install
53
- @aws-sdk/client-dynamodb
54
- @aws-sdk/client-cognito-identity-provider
55
- @aws-sdk/client-secrets-manager
56
- @aws-sdk/util-dynamodb
57
- @opensearch-project/opensearch
54
+ npm install \
55
+ @aws-sdk/client-apigatewaymanagementapi \
56
+ @aws-sdk/client-cognito-identity-provider \
57
+ @aws-sdk/client-dynamodb \
58
+ @aws-sdk/client-secrets-manager \
59
+ @aws-sdk/credential-provider-node \
60
+ @aws-sdk/lib-dynamodb \
61
+ @aws-sdk/util-dynamodb \
62
+ @opensearch-project/opensearch \
63
+ axios dayjs typedi@0.10.0 yup zod
58
64
  ```
59
65
 
60
66
  ## 🏁 Quick Start
@@ -570,6 +576,72 @@ await roleController.assignRole("us-east-1_PoolId", "john@example.com", "Manager
570
576
  - Concurrent requests for the same key are deduplicated (single in-flight fetch)
571
577
  - Cache is automatically invalidated on create, update, and delete operations
572
578
 
579
+ ## 🪪 Caller kinds and policy views (HTTP API + Cognito JWT)
580
+
581
+ Caller mode verifies the `Authorization` JWT itself (REST and HTTP API events alike) and gives every
582
+ request a verified **caller kind**: `guest` (no token), `user`, `retailer`, `supplier` or `admin`.
583
+ Without `CALLER_POOLS` nothing changes: the REST authorizer claims and the legacy access chain are used
584
+ exactly as before.
585
+
586
+ ### `CALLER_POOLS`
587
+
588
+ Set it on the entity config (`CALLER_POOLS`) or as the env variable `CALLER_POOLS` (JSON). The config wins.
589
+
590
+ ```ts
591
+ CALLER_POOLS: [
592
+ { userPoolId: "ap-southeast-1_User", kind: CallerKind.USER },
593
+ { userPoolId: "ap-southeast-1_Supplier", kind: CallerKind.SUPPLIER, clientIds: ["abc123"] },
594
+ { userPoolId: "ap-southeast-1_Admin", kind: CallerKind.ADMIN },
595
+ ]
596
+ ```
597
+
598
+ ```bash
599
+ CALLER_POOLS='[{"userPoolId":"ap-southeast-1_Supplier","kind":"supplier"}]'
600
+ ```
601
+
602
+ - Each entry needs a string `userPoolId` and a `kind` of `user|retailer|supplier|admin`; `clientIds` is
603
+ optional (absent = any app client). Invalid JSON or entries are a config error (HTTP 500).
604
+ - The token's issuer selects the pool. A token from the admin pool is `admin` only with a group from
605
+ `ADMIN_GROUP_NAME`, otherwise `user`.
606
+ - Invalid or expired token: 401. JWKS not reachable: 503.
607
+
608
+ ### `kinds` and `view` on an endpoint policy
609
+
610
+ ```ts
611
+ {
612
+ method: HttpMethod.GET,
613
+ path: "/products",
614
+ kinds: [CallerKind.GUEST, CallerKind.SUPPLIER, CallerKind.ADMIN],
615
+ view: {
616
+ guest: { filter: { status: ["active", "inactive"] }, response: { include: ["id", "name"] } },
617
+ supplier: { filter: { supplierId: claim("custom:profileId") } },
618
+ },
619
+ }
620
+ ```
621
+
622
+ - `kinds` lists the caller kinds allowed on the endpoint (403 otherwise). In caller mode a policy with a
623
+ `view` but without `kinds` is a config error (500).
624
+ - `view[kind].filter` forces filter keys: a scalar means equality, an array means IN, `claim("…")` takes
625
+ the value from the verified token. Client keys, range/operator/index selectors and owner aliases
626
+ (`profileId`, `ownerParentId`, `ownerId`, `OWNER_ID_FIELD_NAME`, `OWNER_PARENT_ID_FIELD_NAME`) that
627
+ could replace a forced key are dropped. By-id GET of an item outside the view is 404, PATCH/PUT/DELETE
628
+ 403; a POST/PUT/PATCH body value for a forced key must match it.
629
+ - An allowed kind **without a view** gets the client filter unchanged (no forced keys).
630
+ - `view[kind].response` replaces `response` for that kind.
631
+ - **`claim()` attributes must be read-only for app clients** (Cognito: not in the app client's write
632
+ attributes). A claim value containing `*` or `,` or starting with `!` is rejected (403), because
633
+ DynamoDB and OpenSearch would read it as an operator.
634
+
635
+ ### Things to know
636
+
637
+ - The guards apply only to the **built-in routes** (`<base>`, `/search`, `/search/query`,
638
+ `/search/query/total-count`, `/scan`, `/{id}`). A custom endpoint (`processCrudRequest`) must honour
639
+ `req.filter` and `req.forcedFilter` itself.
640
+ - `PERMISSION_MAP.kinds` (role-table access) defaults to `[CallerKind.ADMIN]` in caller mode.
641
+ - HTTP API (v2) repeated query parameters other than `searchBy` become a comma list, i.e. an IN filter.
642
+ - `ControllerRequest.resolveRequest` only dispatches REST vs HTTP API events; it does **no caller
643
+ verification**.
644
+
573
645
  ## 🤝 Contributing
574
646
 
575
647
  1. Fork the repository
@@ -1,5 +1,6 @@
1
1
  import { APIResponse, BaseService, HttpMethod, HttpRequest, RequestType } from "../index";
2
2
  import { APIGatewayProxyEvent, APIGatewayProxyWebsocketEventV2, CreateAuthChallengeTriggerEvent, DefineAuthChallengeTriggerEvent, DynamoDBStreamEvent, PostConfirmationTriggerEvent, PreSignUpTriggerEvent, ScheduledEvent, SQSEvent, VerifyAuthChallengeResponseTriggerEvent } from "aws-lambda";
3
+ import { type ApiEvent } from "../utils/http/api-event";
3
4
  import { EntityConfig } from "../model/index.js";
4
5
  /**
5
6
  * @deprecated Use `ControllerApi, ControllerSocket, ControllerStream` instead.
@@ -25,7 +26,7 @@ export declare abstract class BaseController<T extends BaseService<any, any>> {
25
26
  protected config: EntityConfig;
26
27
  protected path: string;
27
28
  protected constructor(baseService: T, config: EntityConfig);
28
- resolveCrudRequest(event: APIGatewayProxyEvent): Promise<APIResponse>;
29
+ resolveCrudRequest(event: ApiEvent): Promise<APIResponse>;
29
30
  resolveSQSEvent(sqsEvent: SQSEvent): Promise<void>;
30
31
  resolveStreamEvent(event: DynamoDBStreamEvent): Promise<void>;
31
32
  resolveScheduledEvent(event: ScheduledEvent): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"base-controller.d.ts","sourceRoot":"","sources":["../../src/controller/base-controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-controller.d.ts","sourceRoot":"","sources":["../../src/controller/base-controller.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseController = void 0;
4
4
  const index_1 = require("../index");
5
5
  const exception_1 = require("../exception");
6
+ const caller_model_1 = require("../model/caller.model");
7
+ const caller_1 = require("../utils/auth/caller");
8
+ const policy_view_1 = require("../utils/auth/policy-view");
9
+ const api_event_1 = require("../utils/http/api-event");
6
10
  const string_util_1 = require("../utils/string.util");
7
11
  /**
8
12
  * @deprecated Use `ControllerApi, ControllerSocket, ControllerStream` instead.
@@ -41,16 +45,43 @@ class BaseController {
41
45
  }
42
46
  async resolveCrudRequest(event) {
43
47
  try {
44
- this.bootstrapRequest(event);
48
+ const pools = this.config.CALLER_POOLS ?? (0, caller_1.parseCallerPoolsEnv)(process.env.CALLER_POOLS);
49
+ const caller = pools?.length
50
+ ? await (0, caller_1.resolveCaller)((0, api_event_1.normalizeApiEvent)(event).headers.authorization, pools, this.config.ADMIN_GROUP_NAME ?? [])
51
+ : undefined;
52
+ this.bootstrapRequest(event, caller);
45
53
  const policy = (0, index_1.findMatchedPolicy)(this.methode, this.path, this.config.ENDPOINT_POLICY);
46
- this.checkPermission(policy?.access);
54
+ if (caller && !policy)
55
+ throw new exception_1.ErrorHttp(index_1.HTTP.NOT_FOUND, "No matching policy found for this endpoint");
56
+ if (caller)
57
+ (0, policy_view_1.assertViewHasKinds)(policy);
58
+ let responseFields = policy?.response;
59
+ const viewPath = !!(caller && policy?.kinds);
60
+ if (viewPath) {
61
+ const filter = (0, policy_view_1.stripForcedSelectors)(policy, caller, this.request.filter ?? {}, this.config);
62
+ const view = (0, policy_view_1.applyPolicyView)(policy, caller, filter);
63
+ this.request.filter = view.filter;
64
+ this.request.responseFields = view.response;
65
+ responseFields = view.response;
66
+ const forced = (0, policy_view_1.resolveForcedFilter)(policy, caller);
67
+ if (Object.keys(forced).length) {
68
+ this.request.forcedFilter = forced;
69
+ this.request.body = (0, policy_view_1.guardForcedBody)(this.methode, this.request.body, forced);
70
+ this.requestBody = this.request.body;
71
+ }
72
+ }
73
+ else {
74
+ this.checkPermission(policy?.access);
75
+ }
47
76
  this.validateRequest(policy?.validator);
48
- this.setUserFilter();
77
+ // The view's forced keys own the filter; the legacy owner filter must not overwrite them
78
+ if (!viewPath)
79
+ this.setUserFilter();
49
80
  const response = await this.handleCrudByMethod(event);
50
- if (!policy?.response) {
81
+ if (!responseFields) {
51
82
  return (0, index_1.createApiResponse)(200, response);
52
83
  }
53
- const filteredResponse = this.filterResponse(response, policy.response);
84
+ const filteredResponse = this.filterResponse(response, responseFields);
54
85
  return (0, index_1.createApiResponse)(200, filteredResponse);
55
86
  }
56
87
  catch (err) {
@@ -205,12 +236,25 @@ class BaseController {
205
236
  return isMethodGet && isExpectedPath;
206
237
  }
207
238
  async handleCrudByMethod(event) {
239
+ const forced = this.request?.forcedFilter;
240
+ if (forced && this.entityId && (this.isUpdateRequest() || this.isDeleteRequest() || this.methode === "PUT")) {
241
+ const item = await this.service.findById(this.entityId);
242
+ if (!item)
243
+ throw new exception_1.ErrorHttp(index_1.HTTP.NOT_FOUND, `Resource with ID ${this.entityId} not found`);
244
+ if (!(0, policy_view_1.matchesForcedFilter)(item, forced))
245
+ throw new exception_1.ErrorHttp(index_1.HTTP.FORBIDDEN, "Access denied");
246
+ }
208
247
  if (this.isUpdateRequest())
209
248
  return this.handleUpdate();
210
249
  if (this.isDeleteRequest())
211
250
  return this.handleDelete();
212
- if (this.isFetchRequest())
213
- return this.handleFetch();
251
+ if (this.isFetchRequest()) {
252
+ const item = await this.handleFetch();
253
+ if (forced && !(0, policy_view_1.matchesForcedFilter)(item, forced)) {
254
+ throw new exception_1.ErrorHttp(index_1.HTTP.NOT_FOUND, `Resource with ID ${this.entityId} not found`);
255
+ }
256
+ return item;
257
+ }
214
258
  if (this.methode === "PUT")
215
259
  return this.handleReplace();
216
260
  if (this.methode === "POST")
@@ -262,17 +306,19 @@ class BaseController {
262
306
  const ownerIdFieldName = this.config.OWNER_ID_FIELD_NAME || "ownerId";
263
307
  this.request.filter[ownerIdFieldName] = this.profileId;
264
308
  }
265
- bootstrapRequest(event) {
309
+ bootstrapRequest(event, caller) {
266
310
  log.debug("event: " + JSON.stringify(event, null, 2));
311
+ const isRest = !(0, api_event_1.isHttpApiEvent)(event);
312
+ // HTTP API events are exposed as-is; subclasses typed for REST must check the event version
267
313
  this.event = event;
268
- this.resource = event.resource || undefined;
269
- this.methode = event.httpMethod;
270
- this.path = event?.requestContext?.resourcePath;
314
+ this.resource = isRest ? event.resource || undefined : undefined;
315
+ this.request = (0, index_1.parseHttpRequest)(event, this.config, caller);
316
+ this.methode = this.request.methode;
317
+ this.path = isRest ? event?.requestContext?.resourcePath : this.request.routePath;
271
318
  this.resourcePath = `${this.methode} ${this.path}`;
272
- this.request = (0, index_1.parseHttpRequest)(event, this.config);
273
319
  this.requestBody = this.request.body;
274
320
  this.entityId = event.pathParameters?.id || undefined;
275
- this.parserUserData(event.requestContext?.authorizer?.claims);
321
+ this.parserUserData(caller ? caller.claims : event.requestContext?.authorizer?.claims, caller);
276
322
  }
277
323
  parseEntity() {
278
324
  if (!this.requestBody)
@@ -290,7 +336,7 @@ class BaseController {
290
336
  throw new exception_1.ErrorHttp(index_1.HTTP.UNPROCESSABLE_ENTITY, "Validation Error", formattedErrors);
291
337
  }
292
338
  }
293
- parserUserData(claims) {
339
+ parserUserData(claims, caller) {
294
340
  // request user data
295
341
  this.groups = (0, string_util_1.toArray)(claims?.["cognito:groups"]);
296
342
  this.username = claims?.["cognito:username"] || "";
@@ -298,12 +344,15 @@ class BaseController {
298
344
  this.profileId = claims?.["custom:profileId"] || this.username;
299
345
  const adminGroups = new Set((0, string_util_1.normalizeLower)((0, string_util_1.toArray)(this.adminGroupNames)));
300
346
  const groups = (0, string_util_1.normalizeLower)((0, string_util_1.toArray)(this.groups));
301
- this.isAdmin = groups.some((g) => adminGroups.has(g));
347
+ // With verified callers only kind ADMIN is admin (a supplier token with an admin group is not)
348
+ this.isAdmin = caller ? caller.kind === caller_model_1.CallerKind.ADMIN : groups.some((g) => adminGroups.has(g));
302
349
  if (this.isAdmin) {
303
350
  this.profileId = this.request.body?.ownerId || "";
304
351
  delete this.request.body?.ownerId;
305
352
  this.requestType = index_1.RequestType.ADMIN;
306
353
  }
354
+ else if (caller)
355
+ this.requestType = caller.kind === caller_model_1.CallerKind.GUEST ? index_1.RequestType.GUEST : index_1.RequestType.USER;
307
356
  else if (this.profileId || this.userId)
308
357
  this.requestType = index_1.RequestType.USER;
309
358
  else
@@ -1 +1 @@
1
- {"version":3,"file":"base-controller.js","sourceRoot":"","sources":["../../src/controller/base-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Access,\n APIResponse,\n BaseService,\n createApiResponse,\n EndpointPolicy,\n findMatchedPolicy,\n formatErrors,\n getTableName,\n HTTP,\n HttpMethod,\n HttpRequest,\n parseEventToAction,\n parseHttpRequest,\n parseImage,\n removeFields,\n RequestType,\n ResponseFields,\n validateWithSchema,\n} from \"../index\";\nimport {\n APIGatewayProxyEvent,\n APIGatewayProxyWebsocketEventV2,\n CreateAuthChallengeTriggerEvent,\n DefineAuthChallengeTriggerEvent,\n DynamoDBStreamEvent,\n PostConfirmationTriggerEvent,\n PreSignUpTriggerEvent,\n ScheduledEvent,\n SQSEvent,\n VerifyAuthChallengeResponseTriggerEvent,\n} from \"aws-lambda\";\nimport { ErrorHttp, refineHttpError } from \"../exception\";\nimport { EntityConfig } from \"@chinggis/types\";\nimport { normalizeLower, toArray, trimSpecialChar } from \"../utils/string.util\";\n\n/**\n * @deprecated Use `ControllerApi, ControllerSocket, ControllerStream` instead.\n */\nexport abstract class BaseController<T extends BaseService<any, any>> {\n protected resourcePath: string;\n protected userId: string;\n protected isAdmin: boolean;\n protected request: HttpRequest;\n protected readonly service: T;\n protected adminGroupNames: string[];\n protected requestBody: any;\n protected triggerEvent: any;\n protected eventAttr: any;\n protected event: APIGatewayProxyEvent;\n protected resource: string;\n protected entityId: string;\n protected methode: HttpMethod;\n protected groups: string[] = [];\n protected username: string;\n protected profileId: string;\n protected requestType: RequestType;\n protected config: EntityConfig;\n protected path: string;\n\n protected constructor(baseService: T, config: EntityConfig) {\n this.service = baseService;\n\n // Store config for use in subclasses\n if (!config) return;\n\n this.config = config;\n // Set adminGroupName from config if available\n if (config.ADMIN_GROUP_NAME) {\n this.adminGroupNames = config.ADMIN_GROUP_NAME;\n }\n\n this.service.setConfig(config);\n }\n\n async resolveCrudRequest(event: APIGatewayProxyEvent): Promise<APIResponse> {\n try {\n this.bootstrapRequest(event);\n\n const policy: EndpointPolicy | undefined = findMatchedPolicy(\n this.methode,\n this.path,\n this.config.ENDPOINT_POLICY,\n );\n\n this.checkPermission(policy?.access);\n this.validateRequest(policy?.validator);\n this.setUserFilter();\n\n const response: any = await this.handleCrudByMethod(event);\n\n if (!policy?.response) {\n return createApiResponse(200, response);\n }\n\n const filteredResponse = this.filterResponse(response, policy.response);\n\n return createApiResponse(200, filteredResponse);\n } catch (err) {\n const error = refineHttpError(err);\n log.info(err);\n if (error.statusCode >= 500) log.error(error);\n else if (error.statusCode >= 400) log.warn(error);\n return createApiResponse(error.statusCode, error.content);\n }\n }\n\n async resolveSQSEvent(sqsEvent: SQSEvent): Promise<void> {\n try {\n await this.processSQSEvent(sqsEvent);\n } catch (error) {\n console.error(\"Error handling SQS event:\", error);\n }\n }\n\n async resolveStreamEvent(event: DynamoDBStreamEvent): Promise<void> {\n try {\n for (const record of event.Records) {\n const oldItem = parseImage(record.dynamodb.OldImage) as T;\n const newItem = parseImage(record.dynamodb.NewImage) as T;\n\n await this.service.processChanges(\n oldItem,\n newItem,\n parseEventToAction(record.eventName),\n getTableName(record.eventSourceARN),\n );\n }\n await this.processStreamEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n async resolveScheduledEvent(event: ScheduledEvent): Promise<void> {\n try {\n await this.processScheduledEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n async resolveWebSocketEvent(event: APIGatewayProxyWebsocketEventV2): Promise<void> {\n try {\n return await this.processWebSocketEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n async resolveTriggerEvent(\n event:\n | CreateAuthChallengeTriggerEvent\n | DefineAuthChallengeTriggerEvent\n | VerifyAuthChallengeResponseTriggerEvent\n | PostConfirmationTriggerEvent\n | PreSignUpTriggerEvent,\n ): Promise<void> {\n try {\n this.triggerEvent = event;\n this.eventAttr = this.triggerEvent.userAttributes;\n return await this.processTriggerEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n setConfig(config: EntityConfig): void {\n this.config = config;\n }\n\n protected async handleList(): Promise<any> {\n if (this.methode === \"GET\" && trimSpecialChar(this.path) === trimSpecialChar(this.config.BASE_PATH)) {\n return await this.service.find(this.request.filter || {});\n }\n\n if (this.methode === \"GET\" && trimSpecialChar(this.path) === trimSpecialChar(this.config.BASE_PATH) + \"/search\") {\n return await this.service.search(this.request.filter || {});\n }\n\n if (this.methode === \"GET\" && trimSpecialChar(this.path) === trimSpecialChar(this.config.BASE_PATH) + \"/scan\") {\n return await this.service.scan(this.request.filter || {});\n }\n }\n\n protected async handleUpdate(): Promise<any> {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot PATCH resource without id field\");\n\n const entity = this.parseEntity();\n\n if (Object.keys(entity).length === 0) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No fields to update\");\n }\n\n // id change is forbidden\n if (\"id\" in entity) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot modify the id field\");\n }\n\n entity.id = this.entityId;\n return this.service.update(entity, this.profileId);\n }\n\n protected async handleDelete(): Promise<boolean> {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot delete resource without id field\");\n\n return this.service.remove(this.entityId, this.profileId);\n }\n\n protected async handleFetch(): Promise<any> {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot fetch resource without id field\");\n\n const result = await this.service.findById(this.entityId, this.profileId);\n\n if (result) return result;\n\n throw new ErrorHttp(HTTP.NOT_FOUND, `Resource with ID ${this.entityId} not found`);\n }\n\n protected async handleReplace() {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot PUT resource without id field\");\n\n const entity = this.parseEntity();\n\n if (!Object.keys(entity).length) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No entity provided for PUT update\");\n }\n\n if (!this.entityId) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot PUT resource without id field\");\n }\n\n entity.id = this.entityId;\n\n return this.service.update(entity, this.profileId);\n }\n\n protected async handlePostCreate() {\n const entity = this.parseEntity();\n if (!Object.keys(entity).length) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No entity payload provided\");\n }\n\n if (!this.isAdmin) entity.profileId = this.profileId;\n else if (!entity.profileId) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No profileId provided\");\n }\n\n return await this.service.save(entity, this.profileId);\n }\n\n protected abstract processCrudRequest(event: APIGatewayProxyEvent): Promise<any>;\n\n /**\n * service дээр processChanges methode ажилсны дараа хийгдэх үйлдлүүд байна\n * @param event - DynamoDBStreamEvent\n * @returns Promise<void>\n * */\n protected abstract processStreamEvent(event: DynamoDBStreamEvent): Promise<void>;\n\n protected abstract processScheduledEvent(event: ScheduledEvent): Promise<void>;\n\n protected abstract processWebSocketEvent(event: APIGatewayProxyWebsocketEventV2): Promise<void>;\n\n protected processTriggerEvent?(\n event:\n | CreateAuthChallengeTriggerEvent\n | DefineAuthChallengeTriggerEvent\n | VerifyAuthChallengeResponseTriggerEvent\n | PostConfirmationTriggerEvent\n | PreSignUpTriggerEvent,\n ): Promise<void> {\n throw new Error(`Method not implemented. event: ${JSON.stringify(event, null, 2)}`);\n }\n\n protected abstract processSQSEvent(sqsEvent: SQSEvent): Promise<any>;\n\n protected isListRequest(): boolean {\n const basePath = trimSpecialChar(this.config.BASE_PATH);\n\n const allowedPaths = [basePath, `${basePath}/search`, `${basePath}/scan`];\n\n const isMethodGet = this.methode === \"GET\";\n const isAllowedPath = allowedPaths.includes(trimSpecialChar(this.path));\n\n return isMethodGet && isAllowedPath;\n }\n\n protected isUpdateRequest(): boolean {\n const expectedPath = `${trimSpecialChar(this.config.BASE_PATH)}/{id}`;\n const isMethodPatch = this.methode === \"PATCH\";\n const isExpectedPath = trimSpecialChar(this.path) === expectedPath;\n\n return isMethodPatch && isExpectedPath;\n }\n\n protected isDeleteRequest(): boolean {\n const expectedPath = `${trimSpecialChar(this.config.BASE_PATH)}/{id}`;\n const isMethodDelete = this.methode === \"DELETE\";\n const isExpectedPath = trimSpecialChar(this.path) === expectedPath;\n\n return isMethodDelete && isExpectedPath;\n }\n\n protected isFetchRequest(): boolean {\n const expectedPath = `${trimSpecialChar(this.config.BASE_PATH)}/{id}`;\n const isMethodGet = this.methode === \"GET\";\n const isExpectedPath = trimSpecialChar(this.path) === expectedPath;\n\n return isMethodGet && isExpectedPath;\n }\n\n private async handleCrudByMethod(event: APIGatewayProxyEvent): Promise<any> {\n if (this.isUpdateRequest()) return this.handleUpdate();\n if (this.isDeleteRequest()) return this.handleDelete();\n if (this.isFetchRequest()) return this.handleFetch();\n if (this.methode === \"PUT\") return this.handleReplace();\n if (this.methode === \"POST\") return this.handlePostCreate();\n if (this.isListRequest()) return this.handleList();\n return this.processCrudRequest(event); // Custom Endpoints\n }\n\n private filterResponse(response: any, responsePolicy: ResponseFields): any {\n if (Array.isArray(response)) {\n return removeFields(response, responsePolicy.include, responsePolicy.exclude);\n }\n\n if (response?.items && Array.isArray(response.items)) {\n response.items = removeFields(response.items, responsePolicy.include, responsePolicy.exclude);\n return response;\n }\n\n return removeFields([response], responsePolicy.include, responsePolicy.exclude)[0];\n }\n\n /**\n * Map RequestType to Access for permission checking\n */\n private getUserAccessLevel(): Access {\n switch (this.requestType) {\n case RequestType.ADMIN:\n return Access.ADMIN;\n case RequestType.USER:\n return Access.USER;\n case RequestType.GUEST:\n return Access.PUBLIC;\n case RequestType.SYSTEM:\n return Access.SYSTEM;\n default:\n return Access.PUBLIC;\n }\n }\n\n /** Check if the user has permission for the current operation */\n private checkPermission(allowedAccess: Access[]) {\n const currentAccessLevel = this.getUserAccessLevel();\n\n // This means USER can access OWNER-level permissions, but the service will verify actual ownership\n const hasPermission =\n allowedAccess.includes(currentAccessLevel) ||\n (this.requestType === RequestType.USER && allowedAccess.includes(Access.OWNER));\n\n if (hasPermission) return;\n\n throw new ErrorHttp(\n HTTP.UNAUTHORIZED,\n `Access denied. Required: ${allowedAccess.join(\", \")}, Current: ${currentAccessLevel}`,\n );\n }\n\n private setUserFilter() {\n if (this.isAdmin && !this.profileId) return;\n const ownerIdFieldName = this.config.OWNER_ID_FIELD_NAME || \"ownerId\";\n this.request.filter[ownerIdFieldName] = this.profileId;\n }\n\n private bootstrapRequest(event: APIGatewayProxyEvent) {\n log.debug(\"event: \" + JSON.stringify(event, null, 2));\n\n this.event = event;\n this.resource = event.resource || undefined;\n\n this.methode = event.httpMethod as HttpMethod;\n this.path = event?.requestContext?.resourcePath;\n this.resourcePath = `${this.methode} ${this.path}`;\n\n this.request = parseHttpRequest(event, this.config);\n this.requestBody = this.request.body;\n this.entityId = event.pathParameters?.id || undefined;\n\n this.parserUserData(event.requestContext?.authorizer?.claims);\n }\n\n private parseEntity(): Record<string, unknown> {\n if (!this.requestBody) return {};\n return typeof this.requestBody === \"string\" ? JSON.parse(this.requestBody) : this.requestBody;\n }\n\n private validateRequest(schema: any) {\n if (!schema) return;\n\n try {\n validateWithSchema(schema, this.requestBody);\n } catch (error) {\n const formattedErrors = formatErrors(error);\n throw new ErrorHttp(HTTP.UNPROCESSABLE_ENTITY, \"Validation Error\", formattedErrors);\n }\n }\n\n private parserUserData(claims: any) {\n // request user data\n this.groups = toArray(claims?.[\"cognito:groups\"]);\n this.username = claims?.[\"cognito:username\"] || \"\";\n this.userId = claims?.sub || \"\";\n this.profileId = claims?.[\"custom:profileId\"] || this.username;\n\n const adminGroups = new Set(normalizeLower(toArray(this.adminGroupNames)));\n const groups = normalizeLower(toArray(this.groups));\n this.isAdmin = groups.some((g) => adminGroups.has(g));\n\n if (this.isAdmin) {\n this.profileId = this.request.body?.ownerId || \"\";\n delete this.request.body?.ownerId;\n this.requestType = RequestType.ADMIN;\n } else if (this.profileId || this.userId) this.requestType = RequestType.USER;\n else this.requestType = RequestType.GUEST;\n\n log.debug(\"groups: \" + JSON.stringify(this.groups, null, 2));\n log.debug(`claims:${JSON.stringify(claims, null, 2)}`);\n log.debug(\n `groups:${this.groups}, isAdmin:${this.isAdmin}, userId:${this.userId}, profileId:${this.profileId}, username:${this.username}, requestType:${this.requestType}`,\n );\n }\n}\n"]}
1
+ {"version":3,"file":"base-controller.js","sourceRoot":"","sources":["../../src/controller/base-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Access,\n APIResponse,\n BaseService,\n createApiResponse,\n EndpointPolicy,\n findMatchedPolicy,\n formatErrors,\n getTableName,\n HTTP,\n HttpMethod,\n HttpRequest,\n parseEventToAction,\n parseHttpRequest,\n parseImage,\n removeFields,\n RequestType,\n ResponseFields,\n validateWithSchema,\n} from \"../index\";\nimport {\n APIGatewayProxyEvent,\n APIGatewayProxyWebsocketEventV2,\n CreateAuthChallengeTriggerEvent,\n DefineAuthChallengeTriggerEvent,\n DynamoDBStreamEvent,\n PostConfirmationTriggerEvent,\n PreSignUpTriggerEvent,\n ScheduledEvent,\n SQSEvent,\n VerifyAuthChallengeResponseTriggerEvent,\n} from \"aws-lambda\";\nimport { ErrorHttp, refineHttpError } from \"../exception\";\nimport { type Caller, CallerKind } from \"../model/caller.model\";\nimport { parseCallerPoolsEnv, resolveCaller } from \"../utils/auth/caller\";\nimport {\n applyPolicyView,\n assertViewHasKinds,\n guardForcedBody,\n matchesForcedFilter,\n resolveForcedFilter,\n stripForcedSelectors,\n} from \"../utils/auth/policy-view\";\nimport { type ApiEvent, isHttpApiEvent, normalizeApiEvent } from \"../utils/http/api-event\";\nimport { EntityConfig } from \"@chinggis/types\";\nimport { normalizeLower, toArray, trimSpecialChar } from \"../utils/string.util\";\n\n/**\n * @deprecated Use `ControllerApi, ControllerSocket, ControllerStream` instead.\n */\nexport abstract class BaseController<T extends BaseService<any, any>> {\n protected resourcePath: string;\n protected userId: string;\n protected isAdmin: boolean;\n protected request: HttpRequest;\n protected readonly service: T;\n protected adminGroupNames: string[];\n protected requestBody: any;\n protected triggerEvent: any;\n protected eventAttr: any;\n protected event: APIGatewayProxyEvent;\n protected resource: string;\n protected entityId: string;\n protected methode: HttpMethod;\n protected groups: string[] = [];\n protected username: string;\n protected profileId: string;\n protected requestType: RequestType;\n protected config: EntityConfig;\n protected path: string;\n\n protected constructor(baseService: T, config: EntityConfig) {\n this.service = baseService;\n\n // Store config for use in subclasses\n if (!config) return;\n\n this.config = config;\n // Set adminGroupName from config if available\n if (config.ADMIN_GROUP_NAME) {\n this.adminGroupNames = config.ADMIN_GROUP_NAME;\n }\n\n this.service.setConfig(config);\n }\n\n async resolveCrudRequest(event: ApiEvent): Promise<APIResponse> {\n try {\n const pools = this.config.CALLER_POOLS ?? parseCallerPoolsEnv(process.env.CALLER_POOLS);\n const caller = pools?.length\n ? await resolveCaller(normalizeApiEvent(event).headers.authorization, pools, this.config.ADMIN_GROUP_NAME ?? [])\n : undefined;\n\n this.bootstrapRequest(event, caller);\n\n const policy: EndpointPolicy | undefined = findMatchedPolicy(\n this.methode,\n this.path,\n this.config.ENDPOINT_POLICY,\n );\n\n if (caller && !policy) throw new ErrorHttp(HTTP.NOT_FOUND, \"No matching policy found for this endpoint\");\n if (caller) assertViewHasKinds(policy);\n\n let responseFields = policy?.response;\n const viewPath = !!(caller && policy?.kinds);\n if (viewPath) {\n const filter = stripForcedSelectors(policy, caller, this.request.filter ?? {}, this.config);\n const view = applyPolicyView(policy, caller, filter);\n this.request.filter = view.filter;\n this.request.responseFields = view.response;\n responseFields = view.response;\n const forced = resolveForcedFilter(policy, caller);\n if (Object.keys(forced).length) {\n this.request.forcedFilter = forced;\n this.request.body = guardForcedBody(this.methode, this.request.body, forced);\n this.requestBody = this.request.body;\n }\n } else {\n this.checkPermission(policy?.access);\n }\n this.validateRequest(policy?.validator);\n // The view's forced keys own the filter; the legacy owner filter must not overwrite them\n if (!viewPath) this.setUserFilter();\n\n const response: any = await this.handleCrudByMethod(event as APIGatewayProxyEvent);\n\n if (!responseFields) {\n return createApiResponse(200, response);\n }\n\n const filteredResponse = this.filterResponse(response, responseFields);\n\n return createApiResponse(200, filteredResponse);\n } catch (err) {\n const error = refineHttpError(err);\n log.info(err);\n if (error.statusCode >= 500) log.error(error);\n else if (error.statusCode >= 400) log.warn(error);\n return createApiResponse(error.statusCode, error.content);\n }\n }\n\n async resolveSQSEvent(sqsEvent: SQSEvent): Promise<void> {\n try {\n await this.processSQSEvent(sqsEvent);\n } catch (error) {\n console.error(\"Error handling SQS event:\", error);\n }\n }\n\n async resolveStreamEvent(event: DynamoDBStreamEvent): Promise<void> {\n try {\n for (const record of event.Records) {\n const oldItem = parseImage(record.dynamodb.OldImage) as T;\n const newItem = parseImage(record.dynamodb.NewImage) as T;\n\n await this.service.processChanges(\n oldItem,\n newItem,\n parseEventToAction(record.eventName),\n getTableName(record.eventSourceARN),\n );\n }\n await this.processStreamEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n async resolveScheduledEvent(event: ScheduledEvent): Promise<void> {\n try {\n await this.processScheduledEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n async resolveWebSocketEvent(event: APIGatewayProxyWebsocketEventV2): Promise<void> {\n try {\n return await this.processWebSocketEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n async resolveTriggerEvent(\n event:\n | CreateAuthChallengeTriggerEvent\n | DefineAuthChallengeTriggerEvent\n | VerifyAuthChallengeResponseTriggerEvent\n | PostConfirmationTriggerEvent\n | PreSignUpTriggerEvent,\n ): Promise<void> {\n try {\n this.triggerEvent = event;\n this.eventAttr = this.triggerEvent.userAttributes;\n return await this.processTriggerEvent(event);\n } catch (error) {\n console.error(\"Error handling request event:\", error);\n }\n }\n\n setConfig(config: EntityConfig): void {\n this.config = config;\n }\n\n protected async handleList(): Promise<any> {\n if (this.methode === \"GET\" && trimSpecialChar(this.path) === trimSpecialChar(this.config.BASE_PATH)) {\n return await this.service.find(this.request.filter || {});\n }\n\n if (this.methode === \"GET\" && trimSpecialChar(this.path) === trimSpecialChar(this.config.BASE_PATH) + \"/search\") {\n return await this.service.search(this.request.filter || {});\n }\n\n if (this.methode === \"GET\" && trimSpecialChar(this.path) === trimSpecialChar(this.config.BASE_PATH) + \"/scan\") {\n return await this.service.scan(this.request.filter || {});\n }\n }\n\n protected async handleUpdate(): Promise<any> {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot PATCH resource without id field\");\n\n const entity = this.parseEntity();\n\n if (Object.keys(entity).length === 0) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No fields to update\");\n }\n\n // id change is forbidden\n if (\"id\" in entity) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot modify the id field\");\n }\n\n entity.id = this.entityId;\n return this.service.update(entity, this.profileId);\n }\n\n protected async handleDelete(): Promise<boolean> {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot delete resource without id field\");\n\n return this.service.remove(this.entityId, this.profileId);\n }\n\n protected async handleFetch(): Promise<any> {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot fetch resource without id field\");\n\n const result = await this.service.findById(this.entityId, this.profileId);\n\n if (result) return result;\n\n throw new ErrorHttp(HTTP.NOT_FOUND, `Resource with ID ${this.entityId} not found`);\n }\n\n protected async handleReplace() {\n if (!this.entityId) throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot PUT resource without id field\");\n\n const entity = this.parseEntity();\n\n if (!Object.keys(entity).length) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No entity provided for PUT update\");\n }\n\n if (!this.entityId) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"Cannot PUT resource without id field\");\n }\n\n entity.id = this.entityId;\n\n return this.service.update(entity, this.profileId);\n }\n\n protected async handlePostCreate() {\n const entity = this.parseEntity();\n if (!Object.keys(entity).length) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No entity payload provided\");\n }\n\n if (!this.isAdmin) entity.profileId = this.profileId;\n else if (!entity.profileId) {\n throw new ErrorHttp(HTTP.BAD_REQUEST, \"No profileId provided\");\n }\n\n return await this.service.save(entity, this.profileId);\n }\n\n protected abstract processCrudRequest(event: APIGatewayProxyEvent): Promise<any>;\n\n /**\n * service дээр processChanges methode ажилсны дараа хийгдэх үйлдлүүд байна\n * @param event - DynamoDBStreamEvent\n * @returns Promise<void>\n * */\n protected abstract processStreamEvent(event: DynamoDBStreamEvent): Promise<void>;\n\n protected abstract processScheduledEvent(event: ScheduledEvent): Promise<void>;\n\n protected abstract processWebSocketEvent(event: APIGatewayProxyWebsocketEventV2): Promise<void>;\n\n protected processTriggerEvent?(\n event:\n | CreateAuthChallengeTriggerEvent\n | DefineAuthChallengeTriggerEvent\n | VerifyAuthChallengeResponseTriggerEvent\n | PostConfirmationTriggerEvent\n | PreSignUpTriggerEvent,\n ): Promise<void> {\n throw new Error(`Method not implemented. event: ${JSON.stringify(event, null, 2)}`);\n }\n\n protected abstract processSQSEvent(sqsEvent: SQSEvent): Promise<any>;\n\n protected isListRequest(): boolean {\n const basePath = trimSpecialChar(this.config.BASE_PATH);\n\n const allowedPaths = [basePath, `${basePath}/search`, `${basePath}/scan`];\n\n const isMethodGet = this.methode === \"GET\";\n const isAllowedPath = allowedPaths.includes(trimSpecialChar(this.path));\n\n return isMethodGet && isAllowedPath;\n }\n\n protected isUpdateRequest(): boolean {\n const expectedPath = `${trimSpecialChar(this.config.BASE_PATH)}/{id}`;\n const isMethodPatch = this.methode === \"PATCH\";\n const isExpectedPath = trimSpecialChar(this.path) === expectedPath;\n\n return isMethodPatch && isExpectedPath;\n }\n\n protected isDeleteRequest(): boolean {\n const expectedPath = `${trimSpecialChar(this.config.BASE_PATH)}/{id}`;\n const isMethodDelete = this.methode === \"DELETE\";\n const isExpectedPath = trimSpecialChar(this.path) === expectedPath;\n\n return isMethodDelete && isExpectedPath;\n }\n\n protected isFetchRequest(): boolean {\n const expectedPath = `${trimSpecialChar(this.config.BASE_PATH)}/{id}`;\n const isMethodGet = this.methode === \"GET\";\n const isExpectedPath = trimSpecialChar(this.path) === expectedPath;\n\n return isMethodGet && isExpectedPath;\n }\n\n private async handleCrudByMethod(event: APIGatewayProxyEvent): Promise<any> {\n const forced = this.request?.forcedFilter;\n if (forced && this.entityId && (this.isUpdateRequest() || this.isDeleteRequest() || this.methode === \"PUT\")) {\n const item = await this.service.findById(this.entityId);\n if (!item) throw new ErrorHttp(HTTP.NOT_FOUND, `Resource with ID ${this.entityId} not found`);\n if (!matchesForcedFilter(item, forced)) throw new ErrorHttp(HTTP.FORBIDDEN, \"Access denied\");\n }\n if (this.isUpdateRequest()) return this.handleUpdate();\n if (this.isDeleteRequest()) return this.handleDelete();\n if (this.isFetchRequest()) {\n const item = await this.handleFetch();\n if (forced && !matchesForcedFilter(item, forced)) {\n throw new ErrorHttp(HTTP.NOT_FOUND, `Resource with ID ${this.entityId} not found`);\n }\n return item;\n }\n if (this.methode === \"PUT\") return this.handleReplace();\n if (this.methode === \"POST\") return this.handlePostCreate();\n if (this.isListRequest()) return this.handleList();\n return this.processCrudRequest(event); // Custom Endpoints\n }\n\n private filterResponse(response: any, responsePolicy: ResponseFields): any {\n if (Array.isArray(response)) {\n return removeFields(response, responsePolicy.include, responsePolicy.exclude);\n }\n\n if (response?.items && Array.isArray(response.items)) {\n response.items = removeFields(response.items, responsePolicy.include, responsePolicy.exclude);\n return response;\n }\n\n return removeFields([response], responsePolicy.include, responsePolicy.exclude)[0];\n }\n\n /**\n * Map RequestType to Access for permission checking\n */\n private getUserAccessLevel(): Access {\n switch (this.requestType) {\n case RequestType.ADMIN:\n return Access.ADMIN;\n case RequestType.USER:\n return Access.USER;\n case RequestType.GUEST:\n return Access.PUBLIC;\n case RequestType.SYSTEM:\n return Access.SYSTEM;\n default:\n return Access.PUBLIC;\n }\n }\n\n /** Check if the user has permission for the current operation */\n private checkPermission(allowedAccess: Access[]) {\n const currentAccessLevel = this.getUserAccessLevel();\n\n // This means USER can access OWNER-level permissions, but the service will verify actual ownership\n const hasPermission =\n allowedAccess.includes(currentAccessLevel) ||\n (this.requestType === RequestType.USER && allowedAccess.includes(Access.OWNER));\n\n if (hasPermission) return;\n\n throw new ErrorHttp(\n HTTP.UNAUTHORIZED,\n `Access denied. Required: ${allowedAccess.join(\", \")}, Current: ${currentAccessLevel}`,\n );\n }\n\n private setUserFilter() {\n if (this.isAdmin && !this.profileId) return;\n const ownerIdFieldName = this.config.OWNER_ID_FIELD_NAME || \"ownerId\";\n this.request.filter[ownerIdFieldName] = this.profileId;\n }\n\n private bootstrapRequest(event: ApiEvent, caller?: Caller) {\n log.debug(\"event: \" + JSON.stringify(event, null, 2));\n\n const isRest = !isHttpApiEvent(event);\n // HTTP API events are exposed as-is; subclasses typed for REST must check the event version\n this.event = event as APIGatewayProxyEvent;\n this.resource = isRest ? (event as APIGatewayProxyEvent).resource || undefined : undefined;\n\n this.request = parseHttpRequest(event, this.config, caller);\n this.methode = this.request.methode;\n this.path = isRest ? (event as APIGatewayProxyEvent)?.requestContext?.resourcePath : this.request.routePath;\n this.resourcePath = `${this.methode} ${this.path}`;\n\n this.requestBody = this.request.body;\n this.entityId = event.pathParameters?.id || undefined;\n\n this.parserUserData(\n caller ? caller.claims : (event as APIGatewayProxyEvent).requestContext?.authorizer?.claims,\n caller,\n );\n }\n\n private parseEntity(): Record<string, unknown> {\n if (!this.requestBody) return {};\n return typeof this.requestBody === \"string\" ? JSON.parse(this.requestBody) : this.requestBody;\n }\n\n private validateRequest(schema: any) {\n if (!schema) return;\n\n try {\n validateWithSchema(schema, this.requestBody);\n } catch (error) {\n const formattedErrors = formatErrors(error);\n throw new ErrorHttp(HTTP.UNPROCESSABLE_ENTITY, \"Validation Error\", formattedErrors);\n }\n }\n\n private parserUserData(claims: any, caller?: Caller) {\n // request user data\n this.groups = toArray(claims?.[\"cognito:groups\"]);\n this.username = claims?.[\"cognito:username\"] || \"\";\n this.userId = claims?.sub || \"\";\n this.profileId = claims?.[\"custom:profileId\"] || this.username;\n\n const adminGroups = new Set(normalizeLower(toArray(this.adminGroupNames)));\n const groups = normalizeLower(toArray(this.groups));\n // With verified callers only kind ADMIN is admin (a supplier token with an admin group is not)\n this.isAdmin = caller ? caller.kind === CallerKind.ADMIN : groups.some((g) => adminGroups.has(g));\n\n if (this.isAdmin) {\n this.profileId = this.request.body?.ownerId || \"\";\n delete this.request.body?.ownerId;\n this.requestType = RequestType.ADMIN;\n } else if (caller) this.requestType = caller.kind === CallerKind.GUEST ? RequestType.GUEST : RequestType.USER;\n else if (this.profileId || this.userId) this.requestType = RequestType.USER;\n else this.requestType = RequestType.GUEST;\n\n log.debug(\"groups: \" + JSON.stringify(this.groups, null, 2));\n log.debug(`claims:${JSON.stringify(claims, null, 2)}`);\n log.debug(\n `groups:${this.groups}, isAdmin:${this.isAdmin}, userId:${this.userId}, profileId:${this.profileId}, username:${this.username}, requestType:${this.requestType}`,\n );\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { APIResponse, BaseEntity, HttpMethod, HttpRequest, List } from "../index";
2
- import { APIGatewayProxyEvent } from "aws-lambda";
2
+ import type { Caller } from "../model/caller.model";
3
+ import { type ApiEvent } from "../utils/http/api-event";
3
4
  import { CognitoUser, EntityConfig } from "../model/index.js";
4
5
  import { CrudService } from "../service";
5
6
  export declare abstract class ControllerApi<R extends BaseEntity, T extends CrudService<R>> {
@@ -8,8 +9,18 @@ export declare abstract class ControllerApi<R extends BaseEntity, T extends Crud
8
9
  protected adminGroupNames: string[];
9
10
  private roleController;
10
11
  protected constructor(baseService: T, config: EntityConfig);
11
- resolveCrudRequest(event: APIGatewayProxyEvent): Promise<APIResponse>;
12
+ resolveCrudRequest(event: ApiEvent): Promise<APIResponse>;
12
13
  setConfig(config: EntityConfig): void;
14
+ /** Verified caller when CALLER_POOLS (config or env) are set; undefined = legacy REST authorizer claims */
15
+ protected resolveRequestCaller(event: ApiEvent): Promise<Caller | undefined>;
16
+ /** Kind check + forced view filter; client index/range selectors and owner aliases on forced keys are dropped */
17
+ private applyCallerPolicy;
18
+ /** `GET <base>/search/query` and `GET <base>/search/query/total-count` (OpenSearch query-string routes) */
19
+ private isSearchQueryPath;
20
+ /** By-id mutations under a forced view: the stored item must exist (404) and match the forced filter (403) */
21
+ private assertForcedItem;
22
+ /** By-id fetch under a forced view: an item outside the view is reported as not found */
23
+ private fetchInView;
13
24
  processCrudRequestPre(req: HttpRequest): Promise<HttpRequest>;
14
25
  processCrudRequestPost(request: HttpRequest, response: R | List<R>): Promise<R | List<R>>;
15
26
  /** Return constructor-defined resource name */
@@ -1 +1 @@
1
- {"version":3,"file":"controller-api.d.ts","sourceRoot":"","sources":["../../src/controller/controller-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"controller-api.d.ts","sourceRoot":"","sources":["../../src/controller/controller-api.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ControllerApi = void 0;
4
4
  const index_1 = require("../index");
5
+ const caller_1 = require("../utils/auth/caller");
6
+ const policy_view_1 = require("../utils/auth/policy-view");
7
+ const api_event_1 = require("../utils/http/api-event");
5
8
  const exception_1 = require("../exception");
6
9
  const string_util_1 = require("../utils/string.util");
7
10
  const controller_role_1 = require("./controller-role");
@@ -25,8 +28,18 @@ class ControllerApi {
25
28
  }
26
29
  async resolveCrudRequest(event) {
27
30
  try {
28
- let req = (0, index_1.parseHttpRequest)(event, this.config);
29
- if (req.policy.access?.length) {
31
+ const caller = await this.resolveRequestCaller(event);
32
+ let req = (0, index_1.parseHttpRequest)(event, this.config, caller);
33
+ if (caller && !req.policy)
34
+ throw new exception_1.ErrorHttp(index_1.HTTP.NOT_FOUND, "No matching policy found for this endpoint");
35
+ if (caller)
36
+ (0, policy_view_1.assertViewHasKinds)(req.policy);
37
+ if (caller && req.policy.kinds) {
38
+ if (req.identity)
39
+ req.identity.isScope = false;
40
+ this.applyCallerPolicy(req, caller);
41
+ }
42
+ else if (req.policy.access?.length) {
30
43
  if (req.identity)
31
44
  req.identity.isScope = false;
32
45
  this.checkPermission(req.policy.access, req);
@@ -35,7 +48,7 @@ class ControllerApi {
35
48
  if (req.identity)
36
49
  req.identity.isScope = true;
37
50
  const resource = this.getResource();
38
- await this.roleController.checkAccess(req, resource, this.config.PERMISSION_MAP.scopeMap, this.adminGroupNames, (id) => this.service.findById(id));
51
+ await this.roleController.checkAccess(req, resource, this.config.PERMISSION_MAP.scopeMap, this.adminGroupNames, (id) => this.service.findById(id), this.config.PERMISSION_MAP.kinds);
39
52
  }
40
53
  else {
41
54
  throw new exception_1.ErrorHttp(index_1.HTTP.FORBIDDEN, "Access denied");
@@ -52,9 +65,10 @@ class ControllerApi {
52
65
  response = await this.processCrudRequestPost(req, response);
53
66
  else
54
67
  response = await this.processCrudRequest(req); // Custom Endpoints
55
- if (!req.policy.response)
68
+ const responseFields = req.responseFields ?? req.policy.response;
69
+ if (!responseFields)
56
70
  return (0, index_1.createApiResponse)(200, response);
57
- const filteredResponse = this.filterResponse(response, req.policy.response);
71
+ const filteredResponse = this.filterResponse(response, responseFields);
58
72
  return (0, index_1.createApiResponse)(200, filteredResponse);
59
73
  }
60
74
  catch (err) {
@@ -69,6 +83,53 @@ class ControllerApi {
69
83
  setConfig(config) {
70
84
  this.config = config;
71
85
  }
86
+ /** Verified caller when CALLER_POOLS (config or env) are set; undefined = legacy REST authorizer claims */
87
+ async resolveRequestCaller(event) {
88
+ const pools = this.config.CALLER_POOLS ?? (0, caller_1.parseCallerPoolsEnv)(process.env.CALLER_POOLS);
89
+ if (!pools?.length)
90
+ return undefined;
91
+ const { headers } = (0, api_event_1.normalizeApiEvent)(event);
92
+ return (0, caller_1.resolveCaller)(headers.authorization, pools, this.config.ADMIN_GROUP_NAME ?? []);
93
+ }
94
+ /** Kind check + forced view filter; client index/range selectors and owner aliases on forced keys are dropped */
95
+ applyCallerPolicy(req, caller) {
96
+ const filter = (0, policy_view_1.stripForcedSelectors)(req.policy, caller, req.filter ?? {}, this.config);
97
+ const view = (0, policy_view_1.applyPolicyView)(req.policy, caller, filter);
98
+ req.filter = view.filter;
99
+ req.responseFields = view.response;
100
+ const forced = (0, policy_view_1.resolveForcedFilter)(req.policy, caller);
101
+ if (Object.keys(forced).length) {
102
+ req.forcedFilter = forced;
103
+ req.body = (0, policy_view_1.guardForcedBody)(req.methode, req.body, forced);
104
+ // OpenSearch search/query* only reads `field__op` keys: the forced keys must be expressed as such
105
+ if (this.isSearchQueryPath(req.routePath))
106
+ req.filter = (0, policy_view_1.applyForcedSearchQuery)(req.filter, forced);
107
+ }
108
+ }
109
+ /** `GET <base>/search/query` and `GET <base>/search/query/total-count` (OpenSearch query-string routes) */
110
+ isSearchQueryPath(path) {
111
+ const base = (0, string_util_1.trimSpecialChar)(this.config.BASE_PATH);
112
+ const normalized = (0, string_util_1.trimSpecialChar)(path ?? "");
113
+ return normalized === `${base}/search/query` || normalized === `${base}/search/query/total-count`;
114
+ }
115
+ /** By-id mutations under a forced view: the stored item must exist (404) and match the forced filter (403) */
116
+ async assertForcedItem(req) {
117
+ if (!req.forcedFilter || !req.entityId)
118
+ return;
119
+ const item = await this.service.findById(req.entityId);
120
+ if (!item)
121
+ throw new exception_1.ErrorHttp(index_1.HTTP.NOT_FOUND, `[CORE] Resource with ID ${req.entityId} not found`);
122
+ if (!(0, policy_view_1.matchesForcedFilter)(item, req.forcedFilter))
123
+ throw new exception_1.ErrorHttp(index_1.HTTP.FORBIDDEN, "Access denied");
124
+ }
125
+ /** By-id fetch under a forced view: an item outside the view is reported as not found */
126
+ async fetchInView(req) {
127
+ const item = await this.handleFetch(req.entityId, req.identity);
128
+ if (req.forcedFilter && !(0, policy_view_1.matchesForcedFilter)(item, req.forcedFilter)) {
129
+ throw new exception_1.ErrorHttp(index_1.HTTP.NOT_FOUND, `[CORE] Resource with ID ${req.entityId} not found`);
130
+ }
131
+ return item;
132
+ }
72
133
  async processCrudRequestPre(req) {
73
134
  return req;
74
135
  }
@@ -223,18 +284,24 @@ class ControllerApi {
223
284
  return isMethodGet && isExpectedPath;
224
285
  }
225
286
  async handleCrudByMethod(req) {
226
- const path = req.event?.requestContext?.resourcePath;
287
+ const path = req.routePath;
227
288
  const entity = this.parseEntity(req.body);
228
289
  if (this.isPermissionRequest(path))
229
290
  return this.handlePermission(req, path);
230
- if (this.isUpdateRequest(req.methode, path))
291
+ if (this.isUpdateRequest(req.methode, path)) {
292
+ await this.assertForcedItem(req);
231
293
  return this.handleUpdate(req.entityId, entity, req.identity);
232
- if (this.isDeleteRequest(req.methode, path))
294
+ }
295
+ if (this.isDeleteRequest(req.methode, path)) {
296
+ await this.assertForcedItem(req);
233
297
  return this.handleDelete(req.entityId, req.identity);
298
+ }
234
299
  if (this.isFetchRequest(req.methode, path))
235
- return this.handleFetch(req.entityId, req.identity);
236
- if (this.isPutRequest(req.methode, path))
300
+ return this.fetchInView(req);
301
+ if (this.isPutRequest(req.methode, path)) {
302
+ await this.assertForcedItem(req);
237
303
  return this.handleReplace(req.entityId, entity, req.identity);
304
+ }
238
305
  if (this.isPostRequest(req.methode, path))
239
306
  return this.handlePostCreate(entity, req.identity);
240
307
  if (this.isListRequest(req.methode, path))