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
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.assertViewHasKinds = assertViewHasKinds;
4
+ exports.applyPolicyView = applyPolicyView;
5
+ exports.stripForcedSelectors = stripForcedSelectors;
6
+ exports.applyForcedSearchQuery = applyForcedSearchQuery;
7
+ exports.resolveForcedFilter = resolveForcedFilter;
8
+ exports.matchesForcedFilter = matchesForcedFilter;
9
+ exports.guardForcedBody = guardForcedBody;
10
+ const errors_1 = require("../../exception/errors");
11
+ const exception_handler_1 = require("../../exception/exception-handler");
12
+ const reflection_util_1 = require("../reflection.util");
13
+ const isClaimRef = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && typeof v.claim === "string";
14
+ /**
15
+ * A claim value that would act as a query operator: DynamoDB treats `*x*`, `x*`, `!x` and `a,b` as
16
+ * contains / begins_with / not-equal / IN, OpenSearch treats `*` and `,` as wildcard and list separators.
17
+ */
18
+ function isOperatorValue(value) {
19
+ return typeof value === "string" && (value.includes("*") || value.includes(",") || value.startsWith("!"));
20
+ }
21
+ /** Caller-mode config check: a policy `view` only takes effect together with `kinds` (500 otherwise) */
22
+ function assertViewHasKinds(policy) {
23
+ if (policy?.view && !policy.kinds) {
24
+ throw new errors_1.ErrorHttp(exception_handler_1.HTTP.INTERNAL_SERVER_ERROR, "policy view requires kinds");
25
+ }
26
+ }
27
+ /** Applies the caller kind check and the per-kind view (forced filters, response fields). Pure. */
28
+ function applyPolicyView(policy, caller, filter) {
29
+ if (policy.kinds && !policy.kinds.includes(caller.kind)) {
30
+ throw new errors_1.ErrorHttp(exception_handler_1.HTTP.FORBIDDEN, "Caller kind is not allowed");
31
+ }
32
+ const view = policy.view?.[caller.kind];
33
+ if (!view)
34
+ return { filter: { ...filter }, response: policy.response };
35
+ const forced = {};
36
+ for (const [key, value] of Object.entries(view.filter ?? {})) {
37
+ if (isClaimRef(value)) {
38
+ const resolved = caller.claims?.[value.claim];
39
+ if (resolved === undefined || resolved === null || resolved === "") {
40
+ throw new errors_1.ErrorHttp(exception_handler_1.HTTP.FORBIDDEN, `Missing claim ${value.claim}`);
41
+ }
42
+ // A claim must be a literal: `*`, `,` and a leading `!` would turn the forced equality into an operator
43
+ if (isOperatorValue(resolved)) {
44
+ throw new errors_1.ErrorHttp(exception_handler_1.HTTP.FORBIDDEN, `Claim ${value.claim} contains a query operator`);
45
+ }
46
+ forced[key] = resolved;
47
+ }
48
+ else {
49
+ // Forced arrays are IN lists: marked so DynamoDB index selection never joins them into a key
50
+ forced[key] = Array.isArray(value) ? (0, reflection_util_1.markForcedIn)([...value]) : value;
51
+ }
52
+ }
53
+ return { filter: { ...filter, ...forced }, response: view.response ?? policy.response };
54
+ }
55
+ const RANGE_SUFFIXES = ["Min", "Max", "From", "To", "Start", "End"];
56
+ /** True when a client filter key selects on forced field `forced` other than by plain equality on `forced` itself */
57
+ function isSelectorOf(key, forced) {
58
+ const lower = key.toLowerCase();
59
+ const forcedLower = forced.toLowerCase();
60
+ if (lower === `min_${forcedLower}` || lower === `max_${forcedLower}`)
61
+ return true;
62
+ if (key.startsWith(`${forced}__`) || key.startsWith(`${forced}.`))
63
+ return true;
64
+ return RANGE_SUFFIXES.some((suffix) => key === `${forced}${suffix}`);
65
+ }
66
+ /** Public owner aliases that the service (`parseOwnerFields`) rewrites onto the configured owner fields */
67
+ const OWNER_ALIASES = ["profileId", "ownerParentId", "ownerId"];
68
+ /** Owner alias and owner field names; the service may copy a client value from one onto another */
69
+ function ownerTargets(owner) {
70
+ return [...OWNER_ALIASES, owner?.OWNER_ID_FIELD_NAME, owner?.OWNER_PARENT_ID_FIELD_NAME].filter((name) => !!name);
71
+ }
72
+ /**
73
+ * When the caller's view forces filter keys, drops client selectors that could bypass or replace them:
74
+ * `indexName`/`indexValue`/`indexSortName`/`indexSortValue` (index choice), range filters on a forced key,
75
+ * `rangeFilterBy/Min/Max` on a forced key, and every key whose base field is a forced key
76
+ * (`min_K`, `max_K`, `K__<op>`, `K.keyword*`, `K` + Min|Max|From|To|Start|End).
77
+ * When a forced key is an owner field (`profileId`, `ownerParentId`, `ownerId`, `owner.OWNER_ID_FIELD_NAME`,
78
+ * `owner.OWNER_PARENT_ID_FIELD_NAME`), all other owner aliases/fields are dropped from the client filter too,
79
+ * because the service maps them onto the owner field after the view and would overwrite the forced value. Pure.
80
+ */
81
+ function stripForcedSelectors(policy, caller, filter, owner) {
82
+ const forcedKeys = Object.keys(policy.view?.[caller.kind]?.filter ?? {});
83
+ const result = { ...(filter ?? {}) };
84
+ if (!forcedKeys.length)
85
+ return result;
86
+ const owners = ownerTargets(owner);
87
+ if (forcedKeys.some((key) => owners.includes(key))) {
88
+ for (const name of owners)
89
+ if (!forcedKeys.includes(name))
90
+ delete result[name];
91
+ }
92
+ delete result.indexName;
93
+ delete result.indexValue;
94
+ delete result.indexSortName;
95
+ delete result.indexSortValue;
96
+ if (forcedKeys.includes(result.rangeFilterBy)) {
97
+ delete result.rangeFilterBy;
98
+ delete result.rangeFilterMin;
99
+ delete result.rangeFilterMax;
100
+ }
101
+ if (Array.isArray(result.rangeFilters)) {
102
+ result.rangeFilters = result.rangeFilters.filter((rf) => !forcedKeys.includes(rf.fieldName));
103
+ if (!result.rangeFilters.length)
104
+ delete result.rangeFilters;
105
+ }
106
+ for (const key of Object.keys(result)) {
107
+ if (forcedKeys.some((forced) => isSelectorOf(key, forced)))
108
+ delete result[key];
109
+ }
110
+ return result;
111
+ }
112
+ /**
113
+ * OpenSearch `search/query*` routes only read `field__op` keys, so a plain forced key would be ignored.
114
+ * Moves every forced key onto `K.keyword__eq` (scalar) or `K.keyword__in` (array, comma-joined) and removes
115
+ * every other client key for that field (`K`, `K__*`, `K.*`). Pure.
116
+ */
117
+ function applyForcedSearchQuery(filter, forced) {
118
+ const result = { ...(filter ?? {}) };
119
+ for (const [key, value] of Object.entries(forced)) {
120
+ for (const name of Object.keys(result)) {
121
+ if (name === key || name.startsWith(`${key}__`) || name.startsWith(`${key}.`))
122
+ delete result[name];
123
+ }
124
+ if (Array.isArray(value))
125
+ result[`${key}.keyword__in`] = value.map(String).join(",");
126
+ else
127
+ result[`${key}.keyword__eq`] = value;
128
+ }
129
+ return result;
130
+ }
131
+ /** Resolved forced filter of the caller's view; empty when the view forces nothing. Throws like applyPolicyView. */
132
+ function resolveForcedFilter(policy, caller) {
133
+ const forcedKeys = Object.keys(policy.view?.[caller.kind]?.filter ?? {});
134
+ if (!forcedKeys.length)
135
+ return {};
136
+ const { filter } = applyPolicyView(policy, caller, {});
137
+ return Object.fromEntries(forcedKeys.map((key) => [key, filter[key]]));
138
+ }
139
+ /** Item matches a forced filter: an array value means membership, anything else equality on the item's field */
140
+ function matchesForcedFilter(item, forced) {
141
+ if (!item)
142
+ return false;
143
+ return Object.entries(forced).every(([key, value]) => Array.isArray(value) ? value.includes(item[key]) : item[key] === value);
144
+ }
145
+ const BODY_METHODS = ["POST", "PUT", "PATCH"];
146
+ /**
147
+ * Guards a POST/PUT/PATCH body under a forced view: a body value for a forced key must match it (403 otherwise);
148
+ * on POST a missing scalar forced key is set from the forced value (array-valued keys are not injected).
149
+ * Returns the (possibly created) body; other methods and non-object bodies are returned unchanged.
150
+ */
151
+ function guardForcedBody(method, body, forced) {
152
+ const upper = String(method ?? "").toUpperCase();
153
+ if (!BODY_METHODS.includes(upper) || !Object.keys(forced).length)
154
+ return body;
155
+ if (body !== undefined && body !== null && (typeof body !== "object" || Array.isArray(body)))
156
+ return body;
157
+ const target = (body ?? {});
158
+ for (const [key, value] of Object.entries(forced)) {
159
+ if (target[key] !== undefined) {
160
+ if (!matchesForcedFilter({ [key]: target[key] }, { [key]: value })) {
161
+ throw new errors_1.ErrorHttp(exception_handler_1.HTTP.FORBIDDEN, `Field ${key} is outside the caller's view`);
162
+ }
163
+ }
164
+ else if (upper === "POST" && !Array.isArray(value)) {
165
+ target[key] = value;
166
+ }
167
+ }
168
+ return body ?? (upper === "POST" && Object.keys(target).length ? target : body);
169
+ }
170
+ //# sourceMappingURL=policy-view.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-view.js","sourceRoot":"","sources":["../../../src/utils/auth/policy-view.ts"],"names":[],"mappings":";;;;;;;;;AAAA,mDAAmD;AACnD,yEAAyD;AAEzD,wDAAkD;AAIlD,MAAM,UAAU,GAAG,CAAC,CAAkB,EAAiB,EAAE,CACvD,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAQ,CAAc,CAAC,KAAK,KAAK,QAAQ,CAAC;AAExG;;;GAGG;AACH,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5G,CAAC;AAED,wGAAwG;AACxG,4BAAmC,MAAkC;IACnE,IAAI,MAAM,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,kBAAS,CAAC,wBAAI,CAAC,qBAAqB,EAAE,4BAA4B,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,mGAAmG;AACnG,yBACE,MAAsB,EACtB,MAAc,EACd,MAAc;IAEd,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,kBAAS,CAAC,wBAAI,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;IAEvE,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;QAC7D,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC9C,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;gBACnE,MAAM,IAAI,kBAAS,CAAC,wBAAI,CAAC,SAAS,EAAE,iBAAiB,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;YACtE,CAAC;YACD,wGAAwG;YACxG,IAAI,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,kBAAS,CAAC,wBAAI,CAAC,SAAS,EAAE,SAAS,KAAK,CAAC,KAAK,4BAA4B,CAAC,CAAC;YACxF,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,6FAA6F;YAC7F,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAA,8BAAY,EAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACxE,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC1F,CAAC;AAED,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;AAEpE,qHAAqH;AACrH,SAAS,YAAY,CAAC,GAAW,EAAE,MAAc;IAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACzC,IAAI,KAAK,KAAK,OAAO,WAAW,EAAE,IAAI,KAAK,KAAK,OAAO,WAAW,EAAE;QAAE,OAAO,IAAI,CAAC;IAClF,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/E,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AACvE,CAAC;AAQD,2GAA2G;AAC3G,MAAM,aAAa,GAAG,CAAC,WAAW,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;AAEhE,mGAAmG;AACnG,SAAS,YAAY,CAAC,KAAuB;IAC3C,OAAO,CAAC,GAAG,aAAa,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,0BAA0B,CAAC,CAAC,MAAM,CAC7F,CAAC,IAAI,EAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,CACjC,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACH,8BACE,MAAsB,EACtB,MAAc,EACd,MAAc,EACd,KAAuB;IAEvB,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;IACzE,MAAM,MAAM,GAAW,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;IAC7C,IAAI,CAAC,UAAU,CAAC,MAAM;QAAE,OAAO,MAAM,CAAC;IAEtC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACnD,KAAK,MAAM,IAAI,IAAI,MAAM;YAAE,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;IACjF,CAAC;IAED,OAAO,MAAM,CAAC,SAAS,CAAC;IACxB,OAAO,MAAM,CAAC,UAAU,CAAC;IACzB,OAAO,MAAM,CAAC,aAAa,CAAC;IAC5B,OAAO,MAAM,CAAC,cAAc,CAAC;IAC7B,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9C,OAAO,MAAM,CAAC,aAAa,CAAC;QAC5B,OAAO,MAAM,CAAC,cAAc,CAAC;QAC7B,OAAO,MAAM,CAAC,cAAc,CAAC;IAC/B,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;QACvC,MAAM,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;QAC7F,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM;YAAE,OAAO,MAAM,CAAC,YAAY,CAAC;IAC9D,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACtC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,gCAAuC,MAAc,EAAE,MAA2B;IAChF,MAAM,MAAM,GAAW,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;IAC7C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACvC,IAAI,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,GAAG,CAAC;gBAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;QACrG,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,MAAM,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;YAChF,MAAM,CAAC,GAAG,GAAG,cAAc,CAAC,GAAG,KAAK,CAAC;IAC5C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,oHAAoH;AACpH,6BAAoC,MAAsB,EAAE,MAAc;IACxE,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;IACzE,IAAI,CAAC,UAAU,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAClC,MAAM,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;IACvD,OAAO,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,gHAAgH;AAChH,6BACE,IAA4C,EAC5C,MAA2B;IAE3B,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CACnD,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CACvE,CAAC;AACJ,CAAC;AAED,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;AAE9C;;;;GAIG;AACH,yBAAgC,MAAc,EAAE,IAAa,EAAE,MAA2B;IACxF,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC9E,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1G,MAAM,MAAM,GAAG,CAAC,IAAI,IAAI,EAAE,CAAwB,CAAC;IACnD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YAC9B,IAAI,CAAC,mBAAmB,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;gBACnE,MAAM,IAAI,kBAAS,CAAC,wBAAI,CAAC,SAAS,EAAE,SAAS,GAAG,+BAA+B,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACrD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAClF,CAAC","sourcesContent":["import { ErrorHttp } from \"../../exception/errors\";\nimport { HTTP } from \"../../exception/exception-handler\";\nimport type { Caller } from \"../../model/caller.model\";\nimport { markForcedIn } from \"../reflection.util\";\nimport type { Filter } from \"../../model/filter.model\";\nimport type { ClaimRef, EndpointPolicy, ResponseFields, ViewFilterValue } from \"../../model/validation.model\";\n\nconst isClaimRef = (v: ViewFilterValue): v is ClaimRef =>\n typeof v === \"object\" && v !== null && !Array.isArray(v) && typeof (v as ClaimRef).claim === \"string\";\n\n/**\n * A claim value that would act as a query operator: DynamoDB treats `*x*`, `x*`, `!x` and `a,b` as\n * contains / begins_with / not-equal / IN, OpenSearch treats `*` and `,` as wildcard and list separators.\n */\nfunction isOperatorValue(value: unknown): boolean {\n return typeof value === \"string\" && (value.includes(\"*\") || value.includes(\",\") || value.startsWith(\"!\"));\n}\n\n/** Caller-mode config check: a policy `view` only takes effect together with `kinds` (500 otherwise) */\nexport function assertViewHasKinds(policy: EndpointPolicy | undefined): void {\n if (policy?.view && !policy.kinds) {\n throw new ErrorHttp(HTTP.INTERNAL_SERVER_ERROR, \"policy view requires kinds\");\n }\n}\n\n/** Applies the caller kind check and the per-kind view (forced filters, response fields). Pure. */\nexport function applyPolicyView(\n policy: EndpointPolicy,\n caller: Caller,\n filter: Filter,\n): { filter: Filter; response?: ResponseFields } {\n if (policy.kinds && !policy.kinds.includes(caller.kind)) {\n throw new ErrorHttp(HTTP.FORBIDDEN, \"Caller kind is not allowed\");\n }\n const view = policy.view?.[caller.kind];\n if (!view) return { filter: { ...filter }, response: policy.response };\n\n const forced: Record<string, any> = {};\n for (const [key, value] of Object.entries(view.filter ?? {})) {\n if (isClaimRef(value)) {\n const resolved = caller.claims?.[value.claim];\n if (resolved === undefined || resolved === null || resolved === \"\") {\n throw new ErrorHttp(HTTP.FORBIDDEN, `Missing claim ${value.claim}`);\n }\n // A claim must be a literal: `*`, `,` and a leading `!` would turn the forced equality into an operator\n if (isOperatorValue(resolved)) {\n throw new ErrorHttp(HTTP.FORBIDDEN, `Claim ${value.claim} contains a query operator`);\n }\n forced[key] = resolved;\n } else {\n // Forced arrays are IN lists: marked so DynamoDB index selection never joins them into a key\n forced[key] = Array.isArray(value) ? markForcedIn([...value]) : value;\n }\n }\n return { filter: { ...filter, ...forced }, response: view.response ?? policy.response };\n}\n\nconst RANGE_SUFFIXES = [\"Min\", \"Max\", \"From\", \"To\", \"Start\", \"End\"];\n\n/** True when a client filter key selects on forced field `forced` other than by plain equality on `forced` itself */\nfunction isSelectorOf(key: string, forced: string): boolean {\n const lower = key.toLowerCase();\n const forcedLower = forced.toLowerCase();\n if (lower === `min_${forcedLower}` || lower === `max_${forcedLower}`) return true;\n if (key.startsWith(`${forced}__`) || key.startsWith(`${forced}.`)) return true;\n return RANGE_SUFFIXES.some((suffix) => key === `${forced}${suffix}`);\n}\n\n/** Entity owner field names (subset of EntityConfig) that the service maps the owner aliases onto */\nexport interface OwnerFieldNames {\n OWNER_ID_FIELD_NAME?: string;\n OWNER_PARENT_ID_FIELD_NAME?: string;\n}\n\n/** Public owner aliases that the service (`parseOwnerFields`) rewrites onto the configured owner fields */\nconst OWNER_ALIASES = [\"profileId\", \"ownerParentId\", \"ownerId\"];\n\n/** Owner alias and owner field names; the service may copy a client value from one onto another */\nfunction ownerTargets(owner?: OwnerFieldNames): string[] {\n return [...OWNER_ALIASES, owner?.OWNER_ID_FIELD_NAME, owner?.OWNER_PARENT_ID_FIELD_NAME].filter(\n (name): name is string => !!name,\n );\n}\n\n/**\n * When the caller's view forces filter keys, drops client selectors that could bypass or replace them:\n * `indexName`/`indexValue`/`indexSortName`/`indexSortValue` (index choice), range filters on a forced key,\n * `rangeFilterBy/Min/Max` on a forced key, and every key whose base field is a forced key\n * (`min_K`, `max_K`, `K__<op>`, `K.keyword*`, `K` + Min|Max|From|To|Start|End).\n * When a forced key is an owner field (`profileId`, `ownerParentId`, `ownerId`, `owner.OWNER_ID_FIELD_NAME`,\n * `owner.OWNER_PARENT_ID_FIELD_NAME`), all other owner aliases/fields are dropped from the client filter too,\n * because the service maps them onto the owner field after the view and would overwrite the forced value. Pure.\n */\nexport function stripForcedSelectors(\n policy: EndpointPolicy,\n caller: Caller,\n filter: Filter,\n owner?: OwnerFieldNames,\n): Filter {\n const forcedKeys = Object.keys(policy.view?.[caller.kind]?.filter ?? {});\n const result: Filter = { ...(filter ?? {}) };\n if (!forcedKeys.length) return result;\n\n const owners = ownerTargets(owner);\n if (forcedKeys.some((key) => owners.includes(key))) {\n for (const name of owners) if (!forcedKeys.includes(name)) delete result[name];\n }\n\n delete result.indexName;\n delete result.indexValue;\n delete result.indexSortName;\n delete result.indexSortValue;\n if (forcedKeys.includes(result.rangeFilterBy)) {\n delete result.rangeFilterBy;\n delete result.rangeFilterMin;\n delete result.rangeFilterMax;\n }\n if (Array.isArray(result.rangeFilters)) {\n result.rangeFilters = result.rangeFilters.filter((rf) => !forcedKeys.includes(rf.fieldName));\n if (!result.rangeFilters.length) delete result.rangeFilters;\n }\n for (const key of Object.keys(result)) {\n if (forcedKeys.some((forced) => isSelectorOf(key, forced))) delete result[key];\n }\n return result;\n}\n\n/**\n * OpenSearch `search/query*` routes only read `field__op` keys, so a plain forced key would be ignored.\n * Moves every forced key onto `K.keyword__eq` (scalar) or `K.keyword__in` (array, comma-joined) and removes\n * every other client key for that field (`K`, `K__*`, `K.*`). Pure.\n */\nexport function applyForcedSearchQuery(filter: Filter, forced: Record<string, any>): Filter {\n const result: Filter = { ...(filter ?? {}) };\n for (const [key, value] of Object.entries(forced)) {\n for (const name of Object.keys(result)) {\n if (name === key || name.startsWith(`${key}__`) || name.startsWith(`${key}.`)) delete result[name];\n }\n if (Array.isArray(value)) result[`${key}.keyword__in`] = value.map(String).join(\",\");\n else result[`${key}.keyword__eq`] = value;\n }\n return result;\n}\n\n/** Resolved forced filter of the caller's view; empty when the view forces nothing. Throws like applyPolicyView. */\nexport function resolveForcedFilter(policy: EndpointPolicy, caller: Caller): Record<string, any> {\n const forcedKeys = Object.keys(policy.view?.[caller.kind]?.filter ?? {});\n if (!forcedKeys.length) return {};\n const { filter } = applyPolicyView(policy, caller, {});\n return Object.fromEntries(forcedKeys.map((key) => [key, filter[key]]));\n}\n\n/** Item matches a forced filter: an array value means membership, anything else equality on the item's field */\nexport function matchesForcedFilter(\n item: Record<string, any> | null | undefined,\n forced: Record<string, any>,\n): boolean {\n if (!item) return false;\n return Object.entries(forced).every(([key, value]) =>\n Array.isArray(value) ? value.includes(item[key]) : item[key] === value,\n );\n}\n\nconst BODY_METHODS = [\"POST\", \"PUT\", \"PATCH\"];\n\n/**\n * Guards a POST/PUT/PATCH body under a forced view: a body value for a forced key must match it (403 otherwise);\n * on POST a missing scalar forced key is set from the forced value (array-valued keys are not injected).\n * Returns the (possibly created) body; other methods and non-object bodies are returned unchanged.\n */\nexport function guardForcedBody(method: string, body: unknown, forced: Record<string, any>): unknown {\n const upper = String(method ?? \"\").toUpperCase();\n if (!BODY_METHODS.includes(upper) || !Object.keys(forced).length) return body;\n if (body !== undefined && body !== null && (typeof body !== \"object\" || Array.isArray(body))) return body;\n\n const target = (body ?? {}) as Record<string, any>;\n for (const [key, value] of Object.entries(forced)) {\n if (target[key] !== undefined) {\n if (!matchesForcedFilter({ [key]: target[key] }, { [key]: value })) {\n throw new ErrorHttp(HTTP.FORBIDDEN, `Field ${key} is outside the caller's view`);\n }\n } else if (upper === \"POST\" && !Array.isArray(value)) {\n target[key] = value;\n }\n }\n return body ?? (upper === \"POST\" && Object.keys(target).length ? target : body);\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import type { HttpMethod } from "../../model/index.js";
2
+ import type { APIGatewayProxyEvent, APIGatewayProxyEventV2 } from "aws-lambda";
3
+ export type ApiEvent = APIGatewayProxyEvent | APIGatewayProxyEventV2;
4
+ export interface NormalizedApiEvent {
5
+ version: "1.0" | "2.0";
6
+ method: HttpMethod;
7
+ /** Route template with leading slash, e.g. "/entities/{id}" */
8
+ routePath: string;
9
+ rawPath: string;
10
+ /** Header names are lower-cased */
11
+ headers: Record<string, string>;
12
+ query: Record<string, string>;
13
+ multiQuery: Record<string, string[]>;
14
+ pathParameters: Record<string, string>;
15
+ /** base64 already decoded */
16
+ body?: string;
17
+ /** REST API (v1) requestContext.authorizer.claims only */
18
+ restClaims?: Record<string, any>;
19
+ raw: ApiEvent;
20
+ }
21
+ export declare function isHttpApiEvent(event: ApiEvent): event is APIGatewayProxyEventV2;
22
+ export declare function normalizeApiEvent(event: ApiEvent): NormalizedApiEvent;
23
+ //# sourceMappingURL=api-event.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-event.d.ts","sourceRoot":"","sources":["../../../src/utils/http/api-event.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE/E,MAAM,MAAM,QAAQ,GAAG,oBAAoB,GAAG,sBAAsB,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,KAAK,GAAG,KAAK,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAE1D,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,GAAG,EAAE,QAAQ,CAAC;CACf;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK,IAAI,sBAAsB,CAE/E;AAuBD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,QAAQ,GAAG,kBAAkB,CAsDrE"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isHttpApiEvent = isHttpApiEvent;
4
+ exports.normalizeApiEvent = normalizeApiEvent;
5
+ function isHttpApiEvent(event) {
6
+ return event.version === "2.0";
7
+ }
8
+ function lowerCaseKeys(headers) {
9
+ const result = {};
10
+ for (const [key, value] of Object.entries(headers ?? {})) {
11
+ if (value !== undefined)
12
+ result[key.toLowerCase()] = value;
13
+ }
14
+ return result;
15
+ }
16
+ function definedEntries(record) {
17
+ const result = {};
18
+ for (const [key, value] of Object.entries(record ?? {})) {
19
+ if (value !== undefined)
20
+ result[key] = value;
21
+ }
22
+ return result;
23
+ }
24
+ function decodeBody(body, isBase64Encoded) {
25
+ if (body === null || body === undefined)
26
+ return undefined;
27
+ return isBase64Encoded ? Buffer.from(body, "base64").toString("utf-8") : body;
28
+ }
29
+ function normalizeApiEvent(event) {
30
+ if (isHttpApiEvent(event)) {
31
+ const query = definedEntries(event.queryStringParameters);
32
+ const multiQuery = {};
33
+ if (event.rawQueryString) {
34
+ const params = new URLSearchParams(event.rawQueryString);
35
+ for (const key of new Set(params.keys()))
36
+ multiQuery[key] = params.getAll(key);
37
+ }
38
+ else {
39
+ for (const [key, value] of Object.entries(query))
40
+ multiQuery[key] = value.split(",");
41
+ }
42
+ const routeKey = event.routeKey ?? "";
43
+ const spaceIdx = routeKey.indexOf(" ");
44
+ const routePath = routeKey === "$default" || spaceIdx < 0 ? event.rawPath : routeKey.slice(spaceIdx + 1);
45
+ return {
46
+ version: "2.0",
47
+ method: event.requestContext.http.method,
48
+ routePath,
49
+ rawPath: event.rawPath,
50
+ headers: lowerCaseKeys(event.headers),
51
+ query,
52
+ multiQuery,
53
+ pathParameters: definedEntries(event.pathParameters),
54
+ body: decodeBody(event.body, event.isBase64Encoded),
55
+ raw: event,
56
+ };
57
+ }
58
+ const multiQuery = {};
59
+ for (const [key, values] of Object.entries(event.multiValueQueryStringParameters ?? {})) {
60
+ if (values)
61
+ multiQuery[key] = values;
62
+ }
63
+ const query = definedEntries(event.queryStringParameters);
64
+ for (const [key, value] of Object.entries(query)) {
65
+ if (!(key in multiQuery))
66
+ multiQuery[key] = [value];
67
+ }
68
+ const resourcePath = event.requestContext?.resourcePath ?? event.resource ?? "";
69
+ const claims = event.requestContext?.authorizer?.claims;
70
+ return {
71
+ version: "1.0",
72
+ method: event.httpMethod,
73
+ routePath: resourcePath.startsWith("/") ? resourcePath : `/${resourcePath}`,
74
+ rawPath: event.path,
75
+ headers: lowerCaseKeys(event.headers),
76
+ query,
77
+ multiQuery,
78
+ pathParameters: definedEntries(event.pathParameters),
79
+ body: decodeBody(event.body, event.isBase64Encoded),
80
+ restClaims: claims ?? undefined,
81
+ raw: event,
82
+ };
83
+ }
84
+ //# sourceMappingURL=api-event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-event.js","sourceRoot":"","sources":["../../../src/utils/http/api-event.ts"],"names":[],"mappings":"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","sourcesContent":["import type { HttpMethod } from \"@chinggis/types\";\nimport type { APIGatewayProxyEvent, APIGatewayProxyEventV2 } from \"aws-lambda\";\n\nexport type ApiEvent = APIGatewayProxyEvent | APIGatewayProxyEventV2;\n\nexport interface NormalizedApiEvent {\n version: \"1.0\" | \"2.0\";\n method: HttpMethod;\n /** Route template with leading slash, e.g. \"/entities/{id}\" */\n routePath: string;\n rawPath: string;\n /** Header names are lower-cased */\n headers: Record<string, string>;\n query: Record<string, string>;\n multiQuery: Record<string, string[]>;\n pathParameters: Record<string, string>;\n /** base64 already decoded */\n body?: string;\n /** REST API (v1) requestContext.authorizer.claims only */\n // biome-ignore lint/suspicious/noExplicitAny: claims are arbitrary JWT payload\n restClaims?: Record<string, any>;\n raw: ApiEvent;\n}\n\nexport function isHttpApiEvent(event: ApiEvent): event is APIGatewayProxyEventV2 {\n return (event as APIGatewayProxyEventV2).version === \"2.0\";\n}\n\nfunction lowerCaseKeys(headers: Record<string, string | undefined> | null | undefined): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(headers ?? {})) {\n if (value !== undefined) result[key.toLowerCase()] = value;\n }\n return result;\n}\n\nfunction definedEntries(record: Record<string, string | undefined> | null | undefined): Record<string, string> {\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(record ?? {})) {\n if (value !== undefined) result[key] = value;\n }\n return result;\n}\n\nfunction decodeBody(body: string | null | undefined, isBase64Encoded: boolean | undefined): string | undefined {\n if (body === null || body === undefined) return undefined;\n return isBase64Encoded ? Buffer.from(body, \"base64\").toString(\"utf-8\") : body;\n}\n\nexport function normalizeApiEvent(event: ApiEvent): NormalizedApiEvent {\n if (isHttpApiEvent(event)) {\n const query = definedEntries(event.queryStringParameters);\n const multiQuery: Record<string, string[]> = {};\n if (event.rawQueryString) {\n const params = new URLSearchParams(event.rawQueryString);\n for (const key of new Set(params.keys())) multiQuery[key] = params.getAll(key);\n } else {\n for (const [key, value] of Object.entries(query)) multiQuery[key] = value.split(\",\");\n }\n\n const routeKey = event.routeKey ?? \"\";\n const spaceIdx = routeKey.indexOf(\" \");\n const routePath = routeKey === \"$default\" || spaceIdx < 0 ? event.rawPath : routeKey.slice(spaceIdx + 1);\n\n return {\n version: \"2.0\",\n method: event.requestContext.http.method as HttpMethod,\n routePath,\n rawPath: event.rawPath,\n headers: lowerCaseKeys(event.headers),\n query,\n multiQuery,\n pathParameters: definedEntries(event.pathParameters),\n body: decodeBody(event.body, event.isBase64Encoded),\n raw: event,\n };\n }\n\n const multiQuery: Record<string, string[]> = {};\n for (const [key, values] of Object.entries(event.multiValueQueryStringParameters ?? {})) {\n if (values) multiQuery[key] = values;\n }\n const query = definedEntries(event.queryStringParameters);\n for (const [key, value] of Object.entries(query)) {\n if (!(key in multiQuery)) multiQuery[key] = [value];\n }\n\n const resourcePath = event.requestContext?.resourcePath ?? event.resource ?? \"\";\n const claims = event.requestContext?.authorizer?.claims;\n\n return {\n version: \"1.0\",\n method: event.httpMethod as HttpMethod,\n routePath: resourcePath.startsWith(\"/\") ? resourcePath : `/${resourcePath}`,\n rawPath: event.path,\n headers: lowerCaseKeys(event.headers),\n query,\n multiQuery,\n pathParameters: definedEntries(event.pathParameters),\n body: decodeBody(event.body, event.isBase64Encoded),\n restClaims: claims ?? undefined,\n raw: event,\n };\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { CognitoUser, EndpointPolicy, EntityConfig, Filter, HttpMethod, RequestType, ResponseFields } from "../../index";
2
- import { APIGatewayProxyEvent } from "aws-lambda";
2
+ import { type Caller } from "../../model/caller.model";
3
+ import { type ApiEvent } from "./api-event";
3
4
  export type APIResponse = {
4
5
  statusCode: number;
5
6
  headers?: Record<string, string>;
@@ -15,7 +16,10 @@ export declare const createApiResponse: (statusCode: number, body: any, options?
15
16
  export interface HttpRequest<B = any, Q = any, P = any> {
16
17
  event: any;
17
18
  methode: HttpMethod;
19
+ /** "<METHOD> <routePath>" (same value as before on REST) */
18
20
  resourcePath: string;
21
+ /** Route template with leading slash, e.g. "/entities/{id}" (REST resourcePath, HTTP API routeKey path) */
22
+ routePath: string;
19
23
  body?: B;
20
24
  queryStringParameters?: Q;
21
25
  filter?: Filter;
@@ -29,8 +33,14 @@ export interface HttpRequest<B = any, Q = any, P = any> {
29
33
  entityId: string;
30
34
  profileId?: string;
31
35
  parentId?: string;
36
+ /** Verified caller; only set when CALLER_POOLS are configured */
37
+ caller?: Caller;
38
+ /** Response fields of the caller's policy view; overrides policy.response */
39
+ responseFields?: ResponseFields;
40
+ /** Resolved forced filter of the caller's policy view (only set when the view forces keys) */
41
+ forcedFilter?: Record<string, any>;
32
42
  }
33
- export declare const parseHttpRequest: (event: APIGatewayProxyEvent, config: EntityConfig) => HttpRequest;
43
+ export declare const parseHttpRequest: (event: ApiEvent, config: EntityConfig, caller?: Caller) => HttpRequest;
34
44
  export declare function parseFilter(params: Record<string, any> | string, searchBy?: string[]): Filter;
35
45
  export declare function toRecord(queryString: string, options?: {
36
46
  parseDates?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"http.util.d.ts","sourceRoot":"","sources":["../../../src/utils/http/http.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAM,EACN,UAAU,EACV,WAAW,EACX,cAAc,EAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGlD,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,kBAG1C,CAAC;AAEF,eAAO,MAAM,iBAAiB,eAChB,MAAM,QACZ,GAAG,YACC,kBAAkB,oBACV,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvC,WAaF,CAAC;AAEF,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG;IACpD,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,UAAU,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,+BAA+B,CAAC,EAAE,CAAC,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,eAAO,MAAM,gBAAgB,UAAW,oBAAoB,UAAU,YAAY,KAAG,WAiFpF,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAwG7F;AAyCD,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiBrG;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,CAM/G"}
1
+ {"version":3,"file":"http.util.d.ts","sourceRoot":"","sources":["../../../src/utils/http/http.util.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAM,EACN,UAAU,EACV,WAAW,EACX,cAAc,EAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,KAAK,MAAM,EAAc,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,KAAK,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAG/D,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,cAAc,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,kBAG1C,CAAC;AAEF,eAAO,MAAM,iBAAiB,eAChB,MAAM,QACZ,GAAG,YACC,kBAAkB,oBACV,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACvC,WAaF,CAAC;AAEF,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG;IACpD,KAAK,EAAE,GAAG,CAAC;IACX,OAAO,EAAE,UAAU,CAAC;IACpB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,2GAA2G;IAC3G,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,+BAA+B,CAAC,EAAE,CAAC,CAAC;IACpC,cAAc,CAAC,EAAE,CAAC,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACpC;AAED,eAAO,MAAM,gBAAgB,UAAW,QAAQ,UAAU,YAAY,WAAW,MAAM,KAAG,WAmGzF,CAAC;AAEF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAwG7F;AAyCD,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiBrG;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,cAAc,CAM/G"}
@@ -5,6 +5,8 @@ exports.parseFilter = parseFilter;
5
5
  exports.toRecord = toRecord;
6
6
  exports.findMatchedPolicy = findMatchedPolicy;
7
7
  const index_1 = require("../../index");
8
+ const caller_model_1 = require("../../model/caller.model");
9
+ const api_event_1 = require("./api-event");
8
10
  const string_util_1 = require("../string.util");
9
11
  exports.DEFAULT_API_RESPONSE_OPTIONS = {
10
12
  origin: "*",
@@ -24,8 +26,12 @@ const createApiResponse = (statusCode, body, options, responseHeaders) => {
24
26
  };
25
27
  };
26
28
  exports.createApiResponse = createApiResponse;
27
- const parseHttpRequest = (event, config) => {
28
- const identity = event?.requestContext?.authorizer?.claims;
29
+ const parseHttpRequest = (event, config, caller) => {
30
+ const normalized = (0, api_event_1.normalizeApiEvent)(event);
31
+ const isRest = normalized.version === "1.0";
32
+ const rawEvent = event;
33
+ // claims are an arbitrary JWT payload (typed any as before)
34
+ const identity = caller ? caller.claims : normalized.restClaims;
29
35
  // request user data
30
36
  const groupsCognito = (0, string_util_1.toArray)(identity?.["cognito:groups"]);
31
37
  const username = identity?.["cognito:username"] || "";
@@ -34,30 +40,49 @@ const parseHttpRequest = (event, config) => {
34
40
  const parentId = identity?.["custom:parentId"];
35
41
  const adminGroups = new Set((0, string_util_1.normalizeLower)((0, string_util_1.toArray)(config.ADMIN_GROUP_NAME)));
36
42
  const groups = (0, string_util_1.normalizeLower)((0, string_util_1.toArray)(groupsCognito));
37
- const isAdmin = groups.some((g) => adminGroups.has(g));
43
+ // With verified callers only the admin pool + admin group (kind ADMIN) is admin; legacy: group check
44
+ const isAdmin = caller ? caller.kind === caller_model_1.CallerKind.ADMIN : groups.some((g) => adminGroups.has(g));
38
45
  let requestType = index_1.RequestType.GUEST;
39
46
  let isParent = false;
40
47
  if (identity?.["custom:isParent"] != null) {
41
48
  isParent = identity["custom:isParent"] === "1";
42
49
  }
43
- if (isAdmin) {
50
+ if (caller) {
51
+ if (caller.kind === caller_model_1.CallerKind.ADMIN)
52
+ requestType = index_1.RequestType.ADMIN;
53
+ else if (caller.kind !== caller_model_1.CallerKind.GUEST)
54
+ requestType = index_1.RequestType.USER;
55
+ }
56
+ else if (isAdmin) {
44
57
  requestType = index_1.RequestType.ADMIN;
45
58
  }
46
59
  else if (profileId || userId)
47
60
  requestType = index_1.RequestType.USER;
48
- const entityId = decodeURIComponent(event?.pathParameters?.id || "");
49
- const eventResourcePath = event?.requestContext?.resourcePath;
50
- const resourcePath = event.httpMethod + " " + eventResourcePath;
61
+ const entityId = decodeURIComponent(normalized.pathParameters?.id || "");
62
+ const routePath = normalized.routePath;
63
+ // REST keeps the exact former value of requestContext.resourcePath
64
+ const eventResourcePath = isRest ? rawEvent?.requestContext?.resourcePath : routePath;
65
+ const resourcePath = normalized.method + " " + eventResourcePath;
51
66
  const customEntries = Object.entries(identity ?? {}).filter(([key]) => key.startsWith("custom:"));
67
+ const queryStringParameters = isRest
68
+ ? rawEvent?.queryStringParameters || undefined
69
+ : Object.keys(normalized.query).length
70
+ ? normalized.query
71
+ : undefined;
72
+ const multiValueQueryStringParameters = isRest
73
+ ? rawEvent?.multiValueQueryStringParameters || undefined
74
+ : Object.keys(normalized.multiQuery).length
75
+ ? normalized.multiQuery
76
+ : undefined;
52
77
  const parsedParams = {
53
78
  event: event,
54
- methode: event.httpMethod,
55
- queryStringParameters: event?.queryStringParameters || undefined,
56
- filter: event?.queryStringParameters
57
- ? parseFilter(event?.queryStringParameters, event.multiValueQueryStringParameters["searchBy"])
79
+ methode: normalized.method,
80
+ queryStringParameters,
81
+ filter: queryStringParameters
82
+ ? parseFilter(queryStringParameters, normalized.multiQuery.searchBy)
58
83
  : {},
59
- multiValueQueryStringParameters: event?.multiValueQueryStringParameters || undefined,
60
- pathParameters: event?.pathParameters || undefined,
84
+ multiValueQueryStringParameters,
85
+ pathParameters: rawEvent?.pathParameters || undefined,
61
86
  body: undefined,
62
87
  identity: identity
63
88
  ? {
@@ -67,24 +92,24 @@ const parseHttpRequest = (event, config) => {
67
92
  parentId,
68
93
  groups,
69
94
  username,
70
- Authorization: event?.headers?.Authorization,
71
- identityId: event && "x-cognito-identity-id" in event.headers ? event.headers["x-cognito-identity-id"] : undefined,
95
+ Authorization: normalized.headers.authorization,
96
+ identityId: normalized.headers["x-cognito-identity-id"],
72
97
  attributes: Object.fromEntries(customEntries.map(([key, value]) => [key.replace("custom:", ""), value])),
73
98
  }
74
99
  : undefined,
75
- // username,
76
- // userId,
77
- // isAdmin,
78
100
  profileId,
79
101
  parentId,
80
102
  requestType,
81
103
  resourcePath,
104
+ routePath,
82
105
  entityId: entityId === "null" || entityId === "undefined" ? profileId : entityId,
83
- // groups,
84
- headers: { ...event?.headers },
85
- policy: findMatchedPolicy(event.httpMethod, eventResourcePath, config.ENDPOINT_POLICY),
106
+ headers: { ...rawEvent?.headers },
107
+ policy: findMatchedPolicy(normalized.method, eventResourcePath, config.ENDPOINT_POLICY),
108
+ caller,
86
109
  };
87
- if (!isAdmin && parsedParams.policy.access && parsedParams.policy.access.includes(index_1.Access.OWNER)) {
110
+ // Legacy keeps the former TypeError on an unmatched policy; caller mode lets the controller answer 404
111
+ const policyAccess = caller ? parsedParams.policy?.access : parsedParams.policy.access;
112
+ if (!isAdmin && policyAccess && policyAccess.includes(index_1.Access.OWNER)) {
88
113
  if (isParent)
89
114
  parsedParams.filter[config.OWNER_PARENT_ID_FIELD_NAME ?? "ownerParentId"] = profileId;
90
115
  else
@@ -92,8 +117,9 @@ const parseHttpRequest = (event, config) => {
92
117
  if (parentId)
93
118
  parsedParams.filter[config.OWNER_PARENT_ID_FIELD_NAME ?? "ownerParentId"] = parentId;
94
119
  }
95
- if (event?.body)
96
- parsedParams.body = typeof event.body === "string" ? JSON.parse(event.body) : event.body;
120
+ const body = normalized.body;
121
+ if (body)
122
+ parsedParams.body = typeof body === "string" ? JSON.parse(body) : body;
97
123
  return parsedParams;
98
124
  };
99
125
  exports.parseHttpRequest = parseHttpRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"http.util.js","sourceRoot":"","sources":["../../../src/utils/http/http.util.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Access,\n CognitoUser,\n EndpointPolicy,\n EntityConfig,\n Filter,\n HttpMethod,\n RequestType,\n ResponseFields,\n SortOrder,\n toCaseInsensitive,\n} from \"../../index\";\nimport { APIGatewayProxyEvent } from \"aws-lambda\";\nimport { getFirst, normalizeLower, toArray, trimSpecialChar } from \"../string.util\";\n\nexport type APIResponse = {\n statusCode: number;\n headers?: Record<string, string>;\n body: string;\n};\n\nexport type APIResponseOptions = {\n origin?: string;\n contentType: string;\n fields?: ResponseFields;\n};\n\nexport const DEFAULT_API_RESPONSE_OPTIONS: APIResponseOptions = {\n origin: \"*\",\n contentType: \"application/json\",\n};\n\nexport const createApiResponse = (\n statusCode: number,\n body: any,\n options?: APIResponseOptions,\n responseHeaders?: Record<string, string>,\n): APIResponse => {\n const origin = options?.origin ?? DEFAULT_API_RESPONSE_OPTIONS.origin;\n const contentType = options?.contentType ?? DEFAULT_API_RESPONSE_OPTIONS.contentType;\n\n return {\n statusCode,\n body: JSON.stringify(body),\n headers: {\n \"Access-Control-Allow-Origin\": origin,\n \"Content-Type\": contentType,\n ...(responseHeaders ?? {}),\n },\n };\n};\n\nexport interface HttpRequest<B = any, Q = any, P = any> {\n event: any;\n methode: HttpMethod;\n resourcePath: string;\n body?: B;\n queryStringParameters?: Q;\n filter?: Filter;\n customQueryParameters?: Record<string, string>;\n multiValueQueryStringParameters?: Q;\n pathParameters?: P;\n identity?: CognitoUser;\n headers?: Record<string, string>;\n requestType: RequestType;\n policy: EndpointPolicy;\n entityId: string;\n profileId?: string;\n parentId?: string;\n}\n\nexport const parseHttpRequest = (event: APIGatewayProxyEvent, config: EntityConfig): HttpRequest => {\n const identity = event?.requestContext?.authorizer?.claims;\n\n // request user data\n const groupsCognito = toArray(identity?.[\"cognito:groups\"]);\n\n const username = identity?.[\"cognito:username\"] || \"\";\n const userId = identity?.sub || \"\";\n const profileId = identity?.profile || username;\n const parentId: string | undefined = identity?.[\"custom:parentId\"];\n\n const adminGroups = new Set(normalizeLower(toArray(config.ADMIN_GROUP_NAME)));\n const groups = normalizeLower(toArray(groupsCognito));\n const isAdmin = groups.some((g) => adminGroups.has(g));\n let requestType: RequestType = RequestType.GUEST;\n let isParent: boolean = false;\n\n if (identity?.[\"custom:isParent\"] != null) {\n isParent = identity[\"custom:isParent\"] === \"1\";\n }\n\n if (isAdmin) {\n requestType = RequestType.ADMIN;\n } else if (profileId || userId) requestType = RequestType.USER;\n\n const entityId = decodeURIComponent(event?.pathParameters?.id || \"\");\n const eventResourcePath = event?.requestContext?.resourcePath;\n const resourcePath = event.httpMethod + \" \" + eventResourcePath;\n const customEntries = Object.entries(identity ?? {}).filter(([key]) => key.startsWith(\"custom:\"));\n\n const parsedParams: HttpRequest = {\n event: event,\n methode: event.httpMethod as HttpMethod,\n queryStringParameters: event?.queryStringParameters || undefined,\n\n filter: event?.queryStringParameters\n ? parseFilter(\n event?.queryStringParameters as Record<string, any>,\n event.multiValueQueryStringParameters[\"searchBy\"],\n )\n : {},\n\n multiValueQueryStringParameters: event?.multiValueQueryStringParameters || undefined,\n pathParameters: event?.pathParameters || undefined,\n body: undefined,\n identity: identity\n ? {\n ...identity,\n isParent,\n isAdmin,\n parentId,\n groups,\n username,\n Authorization: event?.headers?.Authorization,\n identityId:\n event && \"x-cognito-identity-id\" in event.headers ? event.headers[\"x-cognito-identity-id\"] : undefined,\n attributes: Object.fromEntries(customEntries.map(([key, value]) => [key.replace(\"custom:\", \"\"), value])),\n }\n : undefined,\n // username,\n // userId,\n // isAdmin,\n profileId,\n parentId,\n requestType,\n resourcePath,\n entityId: entityId === \"null\" || entityId === \"undefined\" ? profileId : entityId,\n // groups,\n headers: { ...event?.headers },\n policy: findMatchedPolicy(event.httpMethod as HttpMethod, eventResourcePath, config.ENDPOINT_POLICY),\n };\n\n if (!isAdmin && parsedParams.policy.access && parsedParams.policy.access.includes(Access.OWNER)) {\n if (isParent) parsedParams.filter[config.OWNER_PARENT_ID_FIELD_NAME ?? \"ownerParentId\"] = profileId;\n else parsedParams.filter[config.OWNER_ID_FIELD_NAME ?? \"profileId\"] = profileId;\n if (parentId) parsedParams.filter[config.OWNER_PARENT_ID_FIELD_NAME ?? \"ownerParentId\"] = parentId;\n }\n\n if (event?.body) parsedParams.body = typeof event.body === \"string\" ? JSON.parse(event.body) : event.body;\n\n return parsedParams;\n};\n\nexport function parseFilter(params: Record<string, any> | string, searchBy?: string[]): Filter {\n const result: Filter = {};\n\n if (searchBy && searchBy.length > 0) {\n result.searchBy = searchBy;\n }\n\n // If the params is a string (URL query string), parse it first\n let parsedParams: Record<string, any>;\n if (typeof params === \"string\") {\n parsedParams = toRecord(params, { parseDates: false });\n } else {\n parsedParams = params;\n }\n\n parsedParams.searchKeyword = getFirst(parsedParams.searchKeyword);\n\n const parseParam = (key: string, parseFunc: (value: string) => any) => {\n const value = parsedParams[key];\n if (value !== undefined && value !== null && String(value).trim() !== \"\") {\n result[key] = parseFunc(value);\n }\n };\n\n parseParam(\"page\", (value) => Number.parseInt(value, 10));\n parseParam(\"size\", (value) => Number.parseInt(value, 10));\n parseParam(\"sortBy\", (value) => value);\n parseParam(\"sort\", (value) => value as SortOrder);\n // parseParam(\"searchBy\", (value) => searchBy);\n parseParam(\"searchKeyword\", (value) => toCaseInsensitive(value));\n parseParam(\"lastKey\", (value) => value);\n parseParam(\"indexName\", (value) => value);\n parseParam(\"indexValue\", (value) => value);\n\n const parseRangeValue = (value: any) => {\n // If value is already a number, return it\n if (typeof value === \"number\") {\n return value;\n }\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?)?$/.test(value)) {\n return value;\n }\n\n // Convert to string for parsing\n const stringValue = String(value);\n\n // Check if it's a valid number (including decimals)\n const numberValue = Number.parseFloat(stringValue);\n if (!Number.isNaN(numberValue)) {\n // Handle zero values (both \"0\" and \"0.0\" should become 0)\n if (numberValue === 0) {\n return 0;\n }\n // Handle integer values\n if (Number.isInteger(numberValue)) {\n return Number.parseInt(stringValue, 10);\n }\n return numberValue;\n }\n\n return value;\n };\n\n // Initialize rangeFilters array\n result.rangeFilters = [];\n\n // Auto-detect range filter patterns with min_ and max_ prefixes\n for (const key in parsedParams) {\n const value = parsedParams[key];\n if (value !== undefined && value !== null && String(value).trim() !== \"\") {\n // Check for min_ and max_ patterns\n const minMatch = key.match(/^min_(.+)$/i);\n const maxMatch = key.match(/^max_(.+)$/i);\n\n if (minMatch) {\n const fieldName = minMatch[1]; // Remove .toLowerCase() to preserve case\n // Find existing range filter or create new one\n let rangeFilter = result.rangeFilters!.find((rf) => rf.fieldName === fieldName);\n if (!rangeFilter) {\n rangeFilter = { fieldName };\n result.rangeFilters!.push(rangeFilter);\n }\n rangeFilter.minValue = parseRangeValue(value);\n } else if (maxMatch) {\n const fieldName = maxMatch[1]; // Remove .toLowerCase() to preserve case\n // Find existing range filter or create new one\n let rangeFilter = result.rangeFilters!.find((rf) => rf.fieldName === fieldName);\n if (!rangeFilter) {\n rangeFilter = { fieldName };\n result.rangeFilters!.push(rangeFilter);\n }\n rangeFilter.maxValue = parseRangeValue(value);\n } else if (!(key in result)) {\n result[key] = value;\n }\n }\n }\n\n // Clean up empty rangeFilters array\n if (result.rangeFilters!.length === 0) {\n delete result.rangeFilters;\n }\n\n return result;\n}\n\nfunction smartParse(value: string): any {\n if (value.includes(\"*\")) return value;\n if (/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?)?$/.test(value)) {\n const parsedDate = new Date(value);\n if (!Number.isNaN(parsedDate.getTime())) {\n return parsedDate;\n }\n }\n\n // Boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Integer\n if (/^-?\\d+$/.test(value)) {\n if (hasLeadingZeroInteger(value)) return value;\n return parseInt(value, 10);\n }\n\n // Float / Double\n if (/^-?\\d*\\.\\d+$/.test(value)) return parseFloat(value);\n\n if (typeof value === \"string\" && value.includes(\",\")) {\n const valueList = value.split(\",\");\n for (let i = 1; i < valueList.length; i++) {\n valueList[i] = smartParse(valueList[i]);\n }\n return valueList;\n }\n\n // Default: string\n return value;\n}\n\nfunction hasLeadingZeroInteger(value: string): boolean {\n const normalized = value.startsWith(\"-\") ? value.slice(1) : value;\n return normalized.length > 1 && normalized.startsWith(\"0\");\n}\n\nexport function toRecord(queryString: string, options?: { parseDates?: boolean }): Record<string, any> {\n const result: Record<string, any> = {};\n const pairs = queryString.split(\"&\");\n const parseDates = options?.parseDates ?? true;\n\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\");\n if (key && value !== undefined) {\n const decodedValue = decodeURIComponent(value);\n result[key] =\n !parseDates && /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?)?$/.test(decodedValue)\n ? decodedValue\n : smartParse(decodedValue);\n }\n }\n\n return result;\n}\n\nexport function findMatchedPolicy(methode: HttpMethod, path: string, policies: EndpointPolicy[]): EndpointPolicy {\n if (!methode || !path || !policies.length) {\n return null;\n }\n\n return policies.find((policy) => policy.method === methode && trimSpecialChar(policy.path) === trimSpecialChar(path));\n}\n"]}
1
+ {"version":3,"file":"http.util.js","sourceRoot":"","sources":["../../../src/utils/http/http.util.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Access,\n CognitoUser,\n EndpointPolicy,\n EntityConfig,\n Filter,\n HttpMethod,\n RequestType,\n ResponseFields,\n SortOrder,\n toCaseInsensitive,\n} from \"../../index\";\nimport { type Caller, CallerKind } from \"../../model/caller.model\";\nimport { type ApiEvent, normalizeApiEvent } from \"./api-event\";\nimport { getFirst, normalizeLower, toArray, trimSpecialChar } from \"../string.util\";\n\nexport type APIResponse = {\n statusCode: number;\n headers?: Record<string, string>;\n body: string;\n};\n\nexport type APIResponseOptions = {\n origin?: string;\n contentType: string;\n fields?: ResponseFields;\n};\n\nexport const DEFAULT_API_RESPONSE_OPTIONS: APIResponseOptions = {\n origin: \"*\",\n contentType: \"application/json\",\n};\n\nexport const createApiResponse = (\n statusCode: number,\n body: any,\n options?: APIResponseOptions,\n responseHeaders?: Record<string, string>,\n): APIResponse => {\n const origin = options?.origin ?? DEFAULT_API_RESPONSE_OPTIONS.origin;\n const contentType = options?.contentType ?? DEFAULT_API_RESPONSE_OPTIONS.contentType;\n\n return {\n statusCode,\n body: JSON.stringify(body),\n headers: {\n \"Access-Control-Allow-Origin\": origin,\n \"Content-Type\": contentType,\n ...(responseHeaders ?? {}),\n },\n };\n};\n\nexport interface HttpRequest<B = any, Q = any, P = any> {\n event: any;\n methode: HttpMethod;\n /** \"<METHOD> <routePath>\" (same value as before on REST) */\n resourcePath: string;\n /** Route template with leading slash, e.g. \"/entities/{id}\" (REST resourcePath, HTTP API routeKey path) */\n routePath: string;\n body?: B;\n queryStringParameters?: Q;\n filter?: Filter;\n customQueryParameters?: Record<string, string>;\n multiValueQueryStringParameters?: Q;\n pathParameters?: P;\n identity?: CognitoUser;\n headers?: Record<string, string>;\n requestType: RequestType;\n policy: EndpointPolicy;\n entityId: string;\n profileId?: string;\n parentId?: string;\n /** Verified caller; only set when CALLER_POOLS are configured */\n caller?: Caller;\n /** Response fields of the caller's policy view; overrides policy.response */\n responseFields?: ResponseFields;\n /** Resolved forced filter of the caller's policy view (only set when the view forces keys) */\n forcedFilter?: Record<string, any>;\n}\n\nexport const parseHttpRequest = (event: ApiEvent, config: EntityConfig, caller?: Caller): HttpRequest => {\n const normalized = normalizeApiEvent(event);\n const isRest = normalized.version === \"1.0\";\n const rawEvent = event as any;\n // claims are an arbitrary JWT payload (typed any as before)\n const identity: any = caller ? caller.claims : normalized.restClaims;\n\n // request user data\n const groupsCognito = toArray(identity?.[\"cognito:groups\"]);\n\n const username = identity?.[\"cognito:username\"] || \"\";\n const userId = identity?.sub || \"\";\n const profileId = identity?.profile || username;\n const parentId: string | undefined = identity?.[\"custom:parentId\"];\n\n const adminGroups = new Set(normalizeLower(toArray(config.ADMIN_GROUP_NAME)));\n const groups = normalizeLower(toArray(groupsCognito));\n // With verified callers only the admin pool + admin group (kind ADMIN) is admin; legacy: group check\n const isAdmin = caller ? caller.kind === CallerKind.ADMIN : groups.some((g) => adminGroups.has(g));\n let requestType: RequestType = RequestType.GUEST;\n let isParent: boolean = false;\n\n if (identity?.[\"custom:isParent\"] != null) {\n isParent = identity[\"custom:isParent\"] === \"1\";\n }\n\n if (caller) {\n if (caller.kind === CallerKind.ADMIN) requestType = RequestType.ADMIN;\n else if (caller.kind !== CallerKind.GUEST) requestType = RequestType.USER;\n } else if (isAdmin) {\n requestType = RequestType.ADMIN;\n } else if (profileId || userId) requestType = RequestType.USER;\n\n const entityId = decodeURIComponent(normalized.pathParameters?.id || \"\");\n const routePath = normalized.routePath;\n // REST keeps the exact former value of requestContext.resourcePath\n const eventResourcePath = isRest ? rawEvent?.requestContext?.resourcePath : routePath;\n const resourcePath = normalized.method + \" \" + eventResourcePath;\n const customEntries = Object.entries(identity ?? {}).filter(([key]) => key.startsWith(\"custom:\"));\n\n const queryStringParameters = isRest\n ? rawEvent?.queryStringParameters || undefined\n : Object.keys(normalized.query).length\n ? normalized.query\n : undefined;\n const multiValueQueryStringParameters = isRest\n ? rawEvent?.multiValueQueryStringParameters || undefined\n : Object.keys(normalized.multiQuery).length\n ? normalized.multiQuery\n : undefined;\n\n const parsedParams: HttpRequest = {\n event: event,\n methode: normalized.method,\n queryStringParameters,\n\n filter: queryStringParameters\n ? parseFilter(queryStringParameters as Record<string, any>, normalized.multiQuery.searchBy)\n : {},\n\n multiValueQueryStringParameters,\n pathParameters: rawEvent?.pathParameters || undefined,\n body: undefined,\n identity: identity\n ? {\n ...identity,\n isParent,\n isAdmin,\n parentId,\n groups,\n username,\n Authorization: normalized.headers.authorization,\n identityId: normalized.headers[\"x-cognito-identity-id\"],\n attributes: Object.fromEntries(customEntries.map(([key, value]) => [key.replace(\"custom:\", \"\"), value])),\n }\n : undefined,\n profileId,\n parentId,\n requestType,\n resourcePath,\n routePath,\n entityId: entityId === \"null\" || entityId === \"undefined\" ? profileId : entityId,\n headers: { ...rawEvent?.headers },\n policy: findMatchedPolicy(normalized.method, eventResourcePath, config.ENDPOINT_POLICY),\n caller,\n };\n\n // Legacy keeps the former TypeError on an unmatched policy; caller mode lets the controller answer 404\n const policyAccess = caller ? parsedParams.policy?.access : parsedParams.policy.access;\n if (!isAdmin && policyAccess && policyAccess.includes(Access.OWNER)) {\n if (isParent) parsedParams.filter[config.OWNER_PARENT_ID_FIELD_NAME ?? \"ownerParentId\"] = profileId;\n else parsedParams.filter[config.OWNER_ID_FIELD_NAME ?? \"profileId\"] = profileId;\n if (parentId) parsedParams.filter[config.OWNER_PARENT_ID_FIELD_NAME ?? \"ownerParentId\"] = parentId;\n }\n\n const body: any = normalized.body;\n if (body) parsedParams.body = typeof body === \"string\" ? JSON.parse(body) : body;\n\n return parsedParams;\n};\n\nexport function parseFilter(params: Record<string, any> | string, searchBy?: string[]): Filter {\n const result: Filter = {};\n\n if (searchBy && searchBy.length > 0) {\n result.searchBy = searchBy;\n }\n\n // If the params is a string (URL query string), parse it first\n let parsedParams: Record<string, any>;\n if (typeof params === \"string\") {\n parsedParams = toRecord(params, { parseDates: false });\n } else {\n parsedParams = params;\n }\n\n parsedParams.searchKeyword = getFirst(parsedParams.searchKeyword);\n\n const parseParam = (key: string, parseFunc: (value: string) => any) => {\n const value = parsedParams[key];\n if (value !== undefined && value !== null && String(value).trim() !== \"\") {\n result[key] = parseFunc(value);\n }\n };\n\n parseParam(\"page\", (value) => Number.parseInt(value, 10));\n parseParam(\"size\", (value) => Number.parseInt(value, 10));\n parseParam(\"sortBy\", (value) => value);\n parseParam(\"sort\", (value) => value as SortOrder);\n // parseParam(\"searchBy\", (value) => searchBy);\n parseParam(\"searchKeyword\", (value) => toCaseInsensitive(value));\n parseParam(\"lastKey\", (value) => value);\n parseParam(\"indexName\", (value) => value);\n parseParam(\"indexValue\", (value) => value);\n\n const parseRangeValue = (value: any) => {\n // If value is already a number, return it\n if (typeof value === \"number\") {\n return value;\n }\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?)?$/.test(value)) {\n return value;\n }\n\n // Convert to string for parsing\n const stringValue = String(value);\n\n // Check if it's a valid number (including decimals)\n const numberValue = Number.parseFloat(stringValue);\n if (!Number.isNaN(numberValue)) {\n // Handle zero values (both \"0\" and \"0.0\" should become 0)\n if (numberValue === 0) {\n return 0;\n }\n // Handle integer values\n if (Number.isInteger(numberValue)) {\n return Number.parseInt(stringValue, 10);\n }\n return numberValue;\n }\n\n return value;\n };\n\n // Initialize rangeFilters array\n result.rangeFilters = [];\n\n // Auto-detect range filter patterns with min_ and max_ prefixes\n for (const key in parsedParams) {\n const value = parsedParams[key];\n if (value !== undefined && value !== null && String(value).trim() !== \"\") {\n // Check for min_ and max_ patterns\n const minMatch = key.match(/^min_(.+)$/i);\n const maxMatch = key.match(/^max_(.+)$/i);\n\n if (minMatch) {\n const fieldName = minMatch[1]; // Remove .toLowerCase() to preserve case\n // Find existing range filter or create new one\n let rangeFilter = result.rangeFilters!.find((rf) => rf.fieldName === fieldName);\n if (!rangeFilter) {\n rangeFilter = { fieldName };\n result.rangeFilters!.push(rangeFilter);\n }\n rangeFilter.minValue = parseRangeValue(value);\n } else if (maxMatch) {\n const fieldName = maxMatch[1]; // Remove .toLowerCase() to preserve case\n // Find existing range filter or create new one\n let rangeFilter = result.rangeFilters!.find((rf) => rf.fieldName === fieldName);\n if (!rangeFilter) {\n rangeFilter = { fieldName };\n result.rangeFilters!.push(rangeFilter);\n }\n rangeFilter.maxValue = parseRangeValue(value);\n } else if (!(key in result)) {\n result[key] = value;\n }\n }\n }\n\n // Clean up empty rangeFilters array\n if (result.rangeFilters!.length === 0) {\n delete result.rangeFilters;\n }\n\n return result;\n}\n\nfunction smartParse(value: string): any {\n if (value.includes(\"*\")) return value;\n if (/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?)?$/.test(value)) {\n const parsedDate = new Date(value);\n if (!Number.isNaN(parsedDate.getTime())) {\n return parsedDate;\n }\n }\n\n // Boolean\n if (value === \"true\") return true;\n if (value === \"false\") return false;\n\n // Integer\n if (/^-?\\d+$/.test(value)) {\n if (hasLeadingZeroInteger(value)) return value;\n return parseInt(value, 10);\n }\n\n // Float / Double\n if (/^-?\\d*\\.\\d+$/.test(value)) return parseFloat(value);\n\n if (typeof value === \"string\" && value.includes(\",\")) {\n const valueList = value.split(\",\");\n for (let i = 1; i < valueList.length; i++) {\n valueList[i] = smartParse(valueList[i]);\n }\n return valueList;\n }\n\n // Default: string\n return value;\n}\n\nfunction hasLeadingZeroInteger(value: string): boolean {\n const normalized = value.startsWith(\"-\") ? value.slice(1) : value;\n return normalized.length > 1 && normalized.startsWith(\"0\");\n}\n\nexport function toRecord(queryString: string, options?: { parseDates?: boolean }): Record<string, any> {\n const result: Record<string, any> = {};\n const pairs = queryString.split(\"&\");\n const parseDates = options?.parseDates ?? true;\n\n for (const pair of pairs) {\n const [key, value] = pair.split(\"=\");\n if (key && value !== undefined) {\n const decodedValue = decodeURIComponent(value);\n result[key] =\n !parseDates && /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z?)?$/.test(decodedValue)\n ? decodedValue\n : smartParse(decodedValue);\n }\n }\n\n return result;\n}\n\nexport function findMatchedPolicy(methode: HttpMethod, path: string, policies: EndpointPolicy[]): EndpointPolicy {\n if (!methode || !path || !policies.length) {\n return null;\n }\n\n return policies.find((policy) => policy.method === methode && trimSpecialChar(policy.path) === trimSpecialChar(path));\n}\n"]}