@strapi/utils 5.56.0 → 5.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/async.js +2 -2
- package/dist/async.js.map +1 -1
- package/dist/async.mjs +1 -1
- package/dist/async.mjs.map +1 -1
- package/dist/content-types.d.ts.map +1 -1
- package/dist/content-types.js +8 -9
- package/dist/content-types.js.map +1 -1
- package/dist/content-types.mjs +6 -7
- package/dist/content-types.mjs.map +1 -1
- package/dist/convert-query-params.d.ts.map +1 -1
- package/dist/convert-query-params.js +33 -34
- package/dist/convert-query-params.js.map +1 -1
- package/dist/convert-query-params.mjs +23 -24
- package/dist/convert-query-params.mjs.map +1 -1
- package/dist/format-yup-error.js +3 -3
- package/dist/format-yup-error.js.map +1 -1
- package/dist/format-yup-error.mjs +1 -1
- package/dist/format-yup-error.mjs.map +1 -1
- package/dist/hooks.js +3 -3
- package/dist/hooks.js.map +1 -1
- package/dist/hooks.mjs +2 -2
- package/dist/hooks.mjs.map +1 -1
- package/dist/pagination.d.ts.map +1 -1
- package/dist/pagination.js +15 -18
- package/dist/pagination.js.map +1 -1
- package/dist/pagination.mjs +15 -18
- package/dist/pagination.mjs.map +1 -1
- package/dist/policy.d.ts +1 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +1 -3
- package/dist/policy.js.map +1 -1
- package/dist/policy.mjs +1 -3
- package/dist/policy.mjs.map +1 -1
- package/dist/primitives/objects.d.ts +7 -1
- package/dist/primitives/objects.d.ts.map +1 -1
- package/dist/primitives/objects.js +5 -0
- package/dist/primitives/objects.js.map +1 -1
- package/dist/primitives/objects.mjs +5 -1
- package/dist/primitives/objects.mjs.map +1 -1
- package/dist/primitives/strings.d.ts.map +1 -1
- package/dist/primitives/strings.js +3 -7
- package/dist/primitives/strings.js.map +1 -1
- package/dist/primitives/strings.mjs +3 -7
- package/dist/primitives/strings.mjs.map +1 -1
- package/dist/provider-factory.js +4 -4
- package/dist/provider-factory.js.map +1 -1
- package/dist/provider-factory.mjs +1 -1
- package/dist/provider-factory.mjs.map +1 -1
- package/dist/relations.js +2 -2
- package/dist/relations.js.map +1 -1
- package/dist/relations.mjs +1 -1
- package/dist/relations.mjs.map +1 -1
- package/dist/sanitize/index.d.ts.map +1 -1
- package/dist/sanitize/index.js +8 -8
- package/dist/sanitize/index.js.map +1 -1
- package/dist/sanitize/index.mjs +7 -7
- package/dist/sanitize/index.mjs.map +1 -1
- package/dist/sanitize/sanitizers.js +10 -10
- package/dist/sanitize/sanitizers.js.map +1 -1
- package/dist/sanitize/sanitizers.mjs +4 -4
- package/dist/sanitize/sanitizers.mjs.map +1 -1
- package/dist/sanitize/visitors/remove-disallowed-fields.js +4 -4
- package/dist/sanitize/visitors/remove-disallowed-fields.js.map +1 -1
- package/dist/sanitize/visitors/remove-disallowed-fields.mjs +3 -3
- package/dist/sanitize/visitors/remove-disallowed-fields.mjs.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-fields.js +2 -2
- package/dist/sanitize/visitors/remove-restricted-fields.js.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-fields.mjs +2 -2
- package/dist/sanitize/visitors/remove-restricted-fields.mjs.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.js +9 -9
- package/dist/sanitize/visitors/remove-restricted-relations.js.map +1 -1
- package/dist/sanitize/visitors/remove-restricted-relations.mjs +4 -4
- package/dist/sanitize/visitors/remove-restricted-relations.mjs.map +1 -1
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +3 -3
- package/dist/security.js.map +1 -1
- package/dist/security.mjs +3 -3
- package/dist/security.mjs.map +1 -1
- package/dist/set-creator-fields.d.ts +4 -1
- package/dist/set-creator-fields.d.ts.map +1 -1
- package/dist/set-creator-fields.js +7 -4
- package/dist/set-creator-fields.js.map +1 -1
- package/dist/set-creator-fields.mjs +7 -4
- package/dist/set-creator-fields.mjs.map +1 -1
- package/dist/traverse/factory.js +8 -8
- package/dist/traverse/factory.js.map +1 -1
- package/dist/traverse/factory.mjs +8 -8
- package/dist/traverse/factory.mjs.map +1 -1
- package/dist/traverse/query-fields.js +7 -7
- package/dist/traverse/query-fields.js.map +1 -1
- package/dist/traverse/query-fields.mjs +3 -3
- package/dist/traverse/query-fields.mjs.map +1 -1
- package/dist/traverse/query-filters.js +12 -12
- package/dist/traverse/query-filters.js.map +1 -1
- package/dist/traverse/query-filters.mjs +7 -7
- package/dist/traverse/query-filters.mjs.map +1 -1
- package/dist/traverse/query-populate.d.ts.map +1 -1
- package/dist/traverse/query-populate.js +21 -21
- package/dist/traverse/query-populate.js.map +1 -1
- package/dist/traverse/query-populate.mjs +12 -12
- package/dist/traverse/query-populate.mjs.map +1 -1
- package/dist/traverse/query-sort.d.ts.map +1 -1
- package/dist/traverse/query-sort.js +16 -16
- package/dist/traverse/query-sort.js.map +1 -1
- package/dist/traverse/query-sort.mjs +6 -6
- package/dist/traverse/query-sort.mjs.map +1 -1
- package/dist/traverse-entity.d.ts.map +1 -1
- package/dist/traverse-entity.js +18 -18
- package/dist/traverse-entity.js.map +1 -1
- package/dist/traverse-entity.mjs +16 -16
- package/dist/traverse-entity.mjs.map +1 -1
- package/dist/validate/index.d.ts.map +1 -1
- package/dist/validate/index.js +5 -5
- package/dist/validate/index.js.map +1 -1
- package/dist/validate/index.mjs +3 -3
- package/dist/validate/index.mjs.map +1 -1
- package/dist/validate/utils.d.ts +2 -1
- package/dist/validate/utils.d.ts.map +1 -1
- package/dist/validate/utils.js +4 -4
- package/dist/validate/utils.js.map +1 -1
- package/dist/validate/utils.mjs +4 -4
- package/dist/validate/utils.mjs.map +1 -1
- package/dist/validate/validators.d.ts.map +1 -1
- package/dist/validate/validators.js +17 -9
- package/dist/validate/validators.js.map +1 -1
- package/dist/validate/validators.mjs +14 -6
- package/dist/validate/validators.mjs.map +1 -1
- package/dist/validate/visitors/throw-disallowed-fields.js +4 -4
- package/dist/validate/visitors/throw-disallowed-fields.js.map +1 -1
- package/dist/validate/visitors/throw-disallowed-fields.mjs +3 -3
- package/dist/validate/visitors/throw-disallowed-fields.mjs.map +1 -1
- package/dist/validate/visitors/throw-restricted-fields.js +2 -2
- package/dist/validate/visitors/throw-restricted-fields.js.map +1 -1
- package/dist/validate/visitors/throw-restricted-fields.mjs +2 -2
- package/dist/validate/visitors/throw-restricted-fields.mjs.map +1 -1
- package/dist/validate/visitors/throw-restricted-relations.js +6 -6
- package/dist/validate/visitors/throw-restricted-relations.js.map +1 -1
- package/dist/validate/visitors/throw-restricted-relations.mjs +2 -2
- package/dist/validate/visitors/throw-restricted-relations.mjs.map +1 -1
- package/dist/validators.js +3 -3
- package/dist/validators.js.map +1 -1
- package/dist/validators.mjs +3 -3
- package/dist/validators.mjs.map +1 -1
- package/dist/yup.d.ts.map +1 -1
- package/dist/yup.js +2 -3
- package/dist/yup.js.map +1 -1
- package/dist/yup.mjs +3 -4
- package/dist/yup.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isString, toPath } from 'lodash';
|
|
2
2
|
|
|
3
3
|
var removeDisallowedFields = ((allowedFields = null)=>({ key, path: { attribute: path } }, { remove })=>{
|
|
4
4
|
// All fields are allowed
|
|
@@ -6,10 +6,10 @@ var removeDisallowedFields = ((allowedFields = null)=>({ key, path: { attribute:
|
|
|
6
6
|
return;
|
|
7
7
|
}
|
|
8
8
|
// Throw on invalid formats
|
|
9
|
-
if (!(isArray(allowedFields) && allowedFields.every(isString))) {
|
|
9
|
+
if (!(Array.isArray(allowedFields) && allowedFields.every(isString))) {
|
|
10
10
|
throw new TypeError(`Expected array of strings for allowedFields but got "${typeof allowedFields}"`);
|
|
11
11
|
}
|
|
12
|
-
if (
|
|
12
|
+
if (path == null) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
const containedPaths = getContainedPaths(path);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-disallowed-fields.mjs","sources":["../../../src/sanitize/visitors/remove-disallowed-fields.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"remove-disallowed-fields.mjs","sources":["../../../src/sanitize/visitors/remove-disallowed-fields.ts"],"sourcesContent":["import { isString, toPath } from 'lodash';\nimport type { Visitor } from '../../traverse/factory';\n\nexport default (allowedFields: string[] | null = null): Visitor =>\n ({ key, path: { attribute: path } }, { remove }) => {\n // All fields are allowed\n if (allowedFields === null) {\n return;\n }\n\n // Throw on invalid formats\n if (!(Array.isArray(allowedFields) && allowedFields.every(isString))) {\n throw new TypeError(\n `Expected array of strings for allowedFields but got \"${typeof allowedFields}\"`\n );\n }\n\n if (path == null) {\n return;\n }\n\n const containedPaths = getContainedPaths(path);\n\n /**\n * Tells if the current path should be kept or not based\n * on the success of the check functions for any of the allowed paths.\n *\n * The check functions are defined as follow:\n *\n * `containedPaths.includes(p)`\n * @example\n * ```js\n * const path = 'foo.bar.field';\n * const p = 'foo.bar';\n * // it should match\n *\n * const path = 'foo.bar.field';\n * const p = 'bar.foo';\n * // it shouldn't match\n *\n * const path = 'foo.bar';\n * const p = 'foo.bar.field';\n * // it should match but isn't handled by this check\n * ```\n *\n * `p.startsWith(`${path}.`)`\n * @example\n * ```js\n * const path = 'foo.bar';\n * const p = 'foo.bar.field';\n * // it should match\n *\n * const path = 'foo.bar.field';\n * const p = 'bar.foo';\n * // it shouldn't match\n *\n * const path = 'foo.bar.field';\n * const p = 'foo.bar';\n * // it should match but isn't handled by this check\n * ```\n */\n const isPathAllowed = allowedFields.some(\n (p) => containedPaths.includes(p) || p.startsWith(`${path}.`)\n );\n\n if (isPathAllowed) {\n return;\n }\n\n // Remove otherwise\n remove(key);\n };\n\n/**\n * Retrieve the list of allowed paths based on the given path\n *\n * @example\n * ```js\n * const containedPaths = getContainedPaths('foo');\n * // ['foo']\n *\n * * const containedPaths = getContainedPaths('foo.bar');\n * // ['foo', 'foo.bar']\n *\n * * const containedPaths = getContainedPaths('foo.bar.field');\n * // ['foo', 'foo.bar', 'foo.bar.field']\n * ```\n */\nconst getContainedPaths = (path: string) => {\n const parts = toPath(path);\n\n return parts.reduce((acc, value, index, list) => {\n return [...acc, list.slice(0, index + 1).join('.')];\n }, [] as string[]);\n};\n"],"names":["allowedFields","key","path","attribute","remove","Array","isArray","every","isString","TypeError","containedPaths","getContainedPaths","isPathAllowed","some","p","includes","startsWith","parts","toPath","reduce","acc","value","index","list","slice","join"],"mappings":";;AAGA,6BAAe,CAAA,CAACA,aAAAA,GAAiC,IAAI,GACnD,CAAC,EAAEC,GAAG,EAAEC,IAAAA,EAAM,EAAEC,WAAWD,IAAI,EAAE,EAAE,EAAE,EAAEE,MAAM,EAAE,GAAA;;AAE7C,QAAA,IAAIJ,kBAAkB,IAAA,EAAM;AAC1B,YAAA;AACF,QAAA;;QAGA,IAAI,EAAEK,KAAAA,CAAMC,OAAO,CAACN,kBAAkBA,aAAAA,CAAcO,KAAK,CAACC,QAAAA,CAAQ,CAAA,EAAI;YACpE,MAAM,IAAIC,UACR,CAAC,qDAAqD,EAAE,OAAOT,aAAAA,CAAc,CAAC,CAAC,CAAA;AAEnF,QAAA;AAEA,QAAA,IAAIE,QAAQ,IAAA,EAAM;AAChB,YAAA;AACF,QAAA;AAEA,QAAA,MAAMQ,iBAAiBC,iBAAAA,CAAkBT,IAAAA,CAAAA;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCC,QACD,MAAMU,aAAAA,GAAgBZ,aAAAA,CAAca,IAAI,CACtC,CAACC,CAAAA,GAAMJ,cAAAA,CAAeK,QAAQ,CAACD,MAAMA,CAAAA,CAAEE,UAAU,CAAC,CAAA,EAAGd,IAAAA,CAAK,CAAC,CAAC,CAAA,CAAA;AAG9D,QAAA,IAAIU,aAAAA,EAAe;AACjB,YAAA;AACF,QAAA;;QAGAR,MAAAA,CAAOH,GAAAA,CAAAA;AACT,IAAA,CAAA;AAEF;;;;;;;;;;;;;;IAeA,MAAMU,oBAAoB,CAACT,IAAAA,GAAAA;AACzB,IAAA,MAAMe,QAAQC,MAAAA,CAAOhB,IAAAA,CAAAA;AAErB,IAAA,OAAOe,MAAME,MAAM,CAAC,CAACC,GAAAA,EAAKC,OAAOC,KAAAA,EAAOC,IAAAA,GAAAA;QACtC,OAAO;AAAIH,YAAAA,GAAAA,GAAAA;AAAKG,YAAAA,IAAAA,CAAKC,KAAK,CAAC,CAAA,EAAGF,KAAAA,GAAQ,CAAA,CAAA,CAAGG,IAAI,CAAC,GAAA;AAAK,SAAA;AACrD,IAAA,CAAA,EAAG,EAAE,CAAA;AACP,CAAA;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _ = require('lodash');
|
|
4
4
|
|
|
5
5
|
var removeRestrictedFields = ((restrictedFields = null)=>({ key, path: { attribute: path } }, { remove })=>{
|
|
6
6
|
// Remove all fields
|
|
@@ -9,7 +9,7 @@ var removeRestrictedFields = ((restrictedFields = null)=>({ key, path: { attribu
|
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
11
|
// Throw on invalid formats
|
|
12
|
-
if (!(
|
|
12
|
+
if (!(Array.isArray(restrictedFields) && restrictedFields.every(_.isString))) {
|
|
13
13
|
throw new TypeError(`Expected array of strings for restrictedFields but got "${typeof restrictedFields}"`);
|
|
14
14
|
}
|
|
15
15
|
// Remove if an exact match was found
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-restricted-fields.js","sources":["../../../src/sanitize/visitors/remove-restricted-fields.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"remove-restricted-fields.js","sources":["../../../src/sanitize/visitors/remove-restricted-fields.ts"],"sourcesContent":["import { isString } from 'lodash';\nimport type { Visitor } from '../../traverse/factory';\n\nexport default (restrictedFields: string[] | null = null): Visitor =>\n ({ key, path: { attribute: path } }, { remove }) => {\n // Remove all fields\n if (restrictedFields === null) {\n remove(key);\n return;\n }\n\n // Throw on invalid formats\n if (!(Array.isArray(restrictedFields) && restrictedFields.every(isString))) {\n throw new TypeError(\n `Expected array of strings for restrictedFields but got \"${typeof restrictedFields}\"`\n );\n }\n\n // Remove if an exact match was found\n if (restrictedFields.includes(path as string)) {\n remove(key);\n return;\n }\n\n // Remove nested matches\n const isRestrictedNested = restrictedFields.some((allowedPath) =>\n path?.toString().startsWith(`${allowedPath}.`)\n );\n if (isRestrictedNested) {\n remove(key);\n }\n };\n"],"names":["restrictedFields","key","path","attribute","remove","Array","isArray","every","isString","TypeError","includes","isRestrictedNested","some","allowedPath","toString","startsWith"],"mappings":";;;;AAGA,6BAAe,CAAA,CAACA,gBAAAA,GAAoC,IAAI,GACtD,CAAC,EAAEC,GAAG,EAAEC,IAAAA,EAAM,EAAEC,WAAWD,IAAI,EAAE,EAAE,EAAE,EAAEE,MAAM,EAAE,GAAA;;AAE7C,QAAA,IAAIJ,qBAAqB,IAAA,EAAM;YAC7BI,MAAAA,CAAOH,GAAAA,CAAAA;AACP,YAAA;AACF,QAAA;;QAGA,IAAI,EAAEI,KAAAA,CAAMC,OAAO,CAACN,qBAAqBA,gBAAAA,CAAiBO,KAAK,CAACC,UAAAA,CAAQ,CAAA,EAAI;YAC1E,MAAM,IAAIC,UACR,CAAC,wDAAwD,EAAE,OAAOT,gBAAAA,CAAiB,CAAC,CAAC,CAAA;AAEzF,QAAA;;QAGA,IAAIA,gBAAAA,CAAiBU,QAAQ,CAACR,IAAAA,CAAAA,EAAiB;YAC7CE,MAAAA,CAAOH,GAAAA,CAAAA;AACP,YAAA;AACF,QAAA;;AAGA,QAAA,MAAMU,kBAAAA,GAAqBX,gBAAAA,CAAiBY,IAAI,CAAC,CAACC,WAAAA,GAChDX,IAAAA,EAAMY,QAAAA,EAAAA,CAAWC,UAAAA,CAAW,CAAA,EAAGF,WAAAA,CAAY,CAAC,CAAC,CAAA,CAAA;AAE/C,QAAA,IAAIF,kBAAAA,EAAoB;YACtBP,MAAAA,CAAOH,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isString } from 'lodash';
|
|
2
2
|
|
|
3
3
|
var removeRestrictedFields = ((restrictedFields = null)=>({ key, path: { attribute: path } }, { remove })=>{
|
|
4
4
|
// Remove all fields
|
|
@@ -7,7 +7,7 @@ var removeRestrictedFields = ((restrictedFields = null)=>({ key, path: { attribu
|
|
|
7
7
|
return;
|
|
8
8
|
}
|
|
9
9
|
// Throw on invalid formats
|
|
10
|
-
if (!(isArray(restrictedFields) && restrictedFields.every(isString))) {
|
|
10
|
+
if (!(Array.isArray(restrictedFields) && restrictedFields.every(isString))) {
|
|
11
11
|
throw new TypeError(`Expected array of strings for restrictedFields but got "${typeof restrictedFields}"`);
|
|
12
12
|
}
|
|
13
13
|
// Remove if an exact match was found
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-restricted-fields.mjs","sources":["../../../src/sanitize/visitors/remove-restricted-fields.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"remove-restricted-fields.mjs","sources":["../../../src/sanitize/visitors/remove-restricted-fields.ts"],"sourcesContent":["import { isString } from 'lodash';\nimport type { Visitor } from '../../traverse/factory';\n\nexport default (restrictedFields: string[] | null = null): Visitor =>\n ({ key, path: { attribute: path } }, { remove }) => {\n // Remove all fields\n if (restrictedFields === null) {\n remove(key);\n return;\n }\n\n // Throw on invalid formats\n if (!(Array.isArray(restrictedFields) && restrictedFields.every(isString))) {\n throw new TypeError(\n `Expected array of strings for restrictedFields but got \"${typeof restrictedFields}\"`\n );\n }\n\n // Remove if an exact match was found\n if (restrictedFields.includes(path as string)) {\n remove(key);\n return;\n }\n\n // Remove nested matches\n const isRestrictedNested = restrictedFields.some((allowedPath) =>\n path?.toString().startsWith(`${allowedPath}.`)\n );\n if (isRestrictedNested) {\n remove(key);\n }\n };\n"],"names":["restrictedFields","key","path","attribute","remove","Array","isArray","every","isString","TypeError","includes","isRestrictedNested","some","allowedPath","toString","startsWith"],"mappings":";;AAGA,6BAAe,CAAA,CAACA,gBAAAA,GAAoC,IAAI,GACtD,CAAC,EAAEC,GAAG,EAAEC,IAAAA,EAAM,EAAEC,WAAWD,IAAI,EAAE,EAAE,EAAE,EAAEE,MAAM,EAAE,GAAA;;AAE7C,QAAA,IAAIJ,qBAAqB,IAAA,EAAM;YAC7BI,MAAAA,CAAOH,GAAAA,CAAAA;AACP,YAAA;AACF,QAAA;;QAGA,IAAI,EAAEI,KAAAA,CAAMC,OAAO,CAACN,qBAAqBA,gBAAAA,CAAiBO,KAAK,CAACC,QAAAA,CAAQ,CAAA,EAAI;YAC1E,MAAM,IAAIC,UACR,CAAC,wDAAwD,EAAE,OAAOT,gBAAAA,CAAiB,CAAC,CAAC,CAAA;AAEzF,QAAA;;QAGA,IAAIA,gBAAAA,CAAiBU,QAAQ,CAACR,IAAAA,CAAAA,EAAiB;YAC7CE,MAAAA,CAAOH,GAAAA,CAAAA;AACP,YAAA;AACF,QAAA;;AAGA,QAAA,MAAMU,kBAAAA,GAAqBX,gBAAAA,CAAiBY,IAAI,CAAC,CAACC,WAAAA,GAChDX,IAAAA,EAAMY,QAAAA,EAAAA,CAAWC,UAAAA,CAAW,CAAA,EAAGF,WAAAA,CAAY,CAAC,CAAC,CAAA,CAAA;AAE/C,QAAA,IAAIF,kBAAAA,EAAoB;YACtBP,MAAAA,CAAOH,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _ = require('lodash');
|
|
4
4
|
var contentTypes = require('../../content-types.js');
|
|
5
5
|
var relations = require('../../relations.js');
|
|
6
6
|
|
|
@@ -83,13 +83,13 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
|
|
|
83
83
|
} else {
|
|
84
84
|
const newMorphValue = await handleMorphElements(elements);
|
|
85
85
|
if (!newMorphValue.length) {
|
|
86
|
-
if (
|
|
86
|
+
if (Array.isArray(elements) && elements.length === 0) {
|
|
87
87
|
return;
|
|
88
88
|
}
|
|
89
89
|
remove(key);
|
|
90
90
|
return;
|
|
91
91
|
}
|
|
92
|
-
if (
|
|
92
|
+
if (Array.isArray(elements)) {
|
|
93
93
|
set(key, newMorphValue);
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
@@ -97,11 +97,11 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
|
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
99
|
const isMorphMutationPayload = (value)=>{
|
|
100
|
-
return
|
|
100
|
+
return _.isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value);
|
|
101
101
|
};
|
|
102
102
|
const isMorphPopulateCount = (value)=>value === true || value === 'true';
|
|
103
103
|
const isMorphPopulatePayload = (value)=>{
|
|
104
|
-
return
|
|
104
|
+
return _.isObject(value) && !('__type' in value) && 'on' in value && _.isObject(value.on);
|
|
105
105
|
};
|
|
106
106
|
const buildAllowedMorphPopulateFragment = async (subPopulate)=>{
|
|
107
107
|
const newOn = {};
|
|
@@ -115,18 +115,18 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
|
|
|
115
115
|
return newOn;
|
|
116
116
|
};
|
|
117
117
|
const isMorphPopulateAllOrCount = (value)=>{
|
|
118
|
-
return value === true || value === 'true' ||
|
|
118
|
+
return value === true || value === 'true' || _.isObject(value) && 'count' in value && isMorphPopulateCount(value.count) && !('on' in value);
|
|
119
119
|
};
|
|
120
120
|
const isMorphCountOutput = (value)=>{
|
|
121
|
-
return
|
|
121
|
+
return _.isObject(value) && 'count' in value && typeof value.count === 'number' && Object.keys(value).length === 1;
|
|
122
122
|
};
|
|
123
123
|
const handleMorphElements = async (elements)=>{
|
|
124
124
|
const allowedElements = [];
|
|
125
|
-
const elementsToCheck =
|
|
125
|
+
const elementsToCheck = Array.isArray(elements) ? elements : [
|
|
126
126
|
elements
|
|
127
127
|
];
|
|
128
128
|
for (const element of elementsToCheck){
|
|
129
|
-
if (!
|
|
129
|
+
if (!_.isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {
|
|
130
130
|
continue;
|
|
131
131
|
}
|
|
132
132
|
const scopes = ACTIONS_TO_VERIFY.map((action)=>`${element.__type}.${action}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-restricted-relations.js","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Model, RelationOrderingOptions } from '../../types';\nimport type { Visitor } from '../../traverse/factory';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\ntype MorphElement = Record<string, unknown> & { __type: string };\ntype MorphMutationPayload = {\n connect?: unknown;\n set?: unknown;\n disconnect?: unknown;\n options?: Record<string, unknown> | null;\n};\ntype MorphPopulatePayload = {\n on: Record<string, unknown>;\n count?: unknown;\n};\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements = (data as Record<string, MorphArray | MorphElement | MorphMutationPayload>)[\n key\n ];\n\n if (!elements) {\n return;\n }\n\n if (isMorphMutationPayload(elements)) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n const options = elements.options;\n if (options && typeof options === 'object') {\n const filteredOptions: RelationOrderingOptions = {};\n\n Object.keys(options).forEach((key) => {\n const optionKey = key as keyof RelationOrderingOptions;\n const validator = VALID_RELATION_ORDERING_KEYS[optionKey];\n const optionValue = options[key];\n\n if (validator && validator(optionValue)) {\n filteredOptions[optionKey] = optionValue as RelationOrderingOptions[typeof optionKey];\n }\n });\n\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else if (isMorphPopulatePayload(elements)) {\n const newOn: Record<string, unknown> = {};\n\n for (const [uid, subPopulate] of Object.entries(elements.on)) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n const count = isMorphPopulateCount(elements.count) ? true : elements.count;\n set(key, { ...elements, count, on: newOn });\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (!newMorphValue.length) {\n if (isArray(elements) && elements.length === 0) {\n return;\n }\n\n remove(key);\n return;\n }\n\n if (isArray(elements)) {\n set(key, newMorphValue);\n return;\n }\n\n set(key, newMorphValue[0]);\n }\n };\n\n const isMorphMutationPayload = (value: unknown): value is MorphMutationPayload => {\n return isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value);\n };\n\n const isMorphPopulateCount = (value: unknown) => value === true || value === 'true';\n\n const isMorphPopulatePayload = (value: unknown): value is MorphPopulatePayload => {\n return isObject(value) && !('__type' in value) && 'on' in value && isObject(value.on);\n };\n\n const buildAllowedMorphPopulateFragment = async (subPopulate: unknown) => {\n const newOn: Record<string, unknown> = {};\n\n for (const uid of getRegisteredContentTypeUIDs()) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n return newOn;\n };\n\n const isMorphPopulateAllOrCount = (value: unknown): value is true | 'true' | { count: true } => {\n return (\n value === true ||\n value === 'true' ||\n (isObject(value) && 'count' in value && isMorphPopulateCount(value.count) && !('on' in value))\n );\n };\n\n const isMorphCountOutput = (value: unknown): value is { count: number } => {\n return (\n isObject(value) &&\n 'count' in value &&\n typeof value.count === 'number' &&\n Object.keys(value).length === 1\n );\n };\n\n const handleMorphElements = async (elements: unknown) => {\n const allowedElements: Record<string, unknown>[] = [];\n const elementsToCheck = isArray(elements) ? elements : [elements];\n\n for (const element of elementsToCheck) {\n if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n const value = (data as Record<string, unknown>)[key];\n\n if (isMorphPopulatePayload(value)) {\n await handleMorphRelation();\n return;\n }\n\n if (isMorphPopulateAllOrCount(value)) {\n const newOn = await buildAllowedMorphPopulateFragment(true);\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n set(\n key,\n value === true || value === 'true' ? { on: newOn } : { ...value, count: true, on: newOn }\n );\n return;\n }\n\n if (isMorphCountOutput(value)) {\n return;\n }\n\n await handleMorphRelation();\n return;\n }\n\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n return;\n }\n\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n\nconst getRegisteredContentTypeUIDs = () => {\n return (Object.values(strapi.contentTypes ?? {}) as Model[])\n .map((model) => model.uid)\n .filter((uid): uid is string => typeof uid === 'string');\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","isMorphMutationPayload","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","optionKey","validator","VALID_RELATION_ORDERING_KEYS","optionValue","isMorphPopulatePayload","newOn","uid","subPopulate","entries","on","scopes","map","action","isAllowed","hasAccessToSomeScopes","count","isMorphPopulateCount","newMorphValue","isArray","value","isObject","buildAllowedMorphPopulateFragment","getRegisteredContentTypeUIDs","isMorphPopulateAllOrCount","isMorphCountOutput","allowedElements","elementsToCheck","element","__type","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify","values","contentTypes","model","filter"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remove-restricted-relations.js","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isObject } from 'lodash';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Model, RelationOrderingOptions } from '../../types';\nimport type { Visitor } from '../../traverse/factory';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\ntype MorphElement = Record<string, unknown> & { __type: string };\ntype MorphMutationPayload = {\n connect?: unknown;\n set?: unknown;\n disconnect?: unknown;\n options?: Record<string, unknown> | null;\n};\ntype MorphPopulatePayload = {\n on: Record<string, unknown>;\n count?: unknown;\n};\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements = (data as Record<string, MorphArray | MorphElement | MorphMutationPayload>)[\n key\n ];\n\n if (!elements) {\n return;\n }\n\n if (isMorphMutationPayload(elements)) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n const options = elements.options;\n if (options && typeof options === 'object') {\n const filteredOptions: RelationOrderingOptions = {};\n\n Object.keys(options).forEach((key) => {\n const optionKey = key as keyof RelationOrderingOptions;\n const validator = VALID_RELATION_ORDERING_KEYS[optionKey];\n const optionValue = options[key];\n\n if (validator && validator(optionValue)) {\n filteredOptions[optionKey] = optionValue as RelationOrderingOptions[typeof optionKey];\n }\n });\n\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else if (isMorphPopulatePayload(elements)) {\n const newOn: Record<string, unknown> = {};\n\n for (const [uid, subPopulate] of Object.entries(elements.on)) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n const count = isMorphPopulateCount(elements.count) ? true : elements.count;\n set(key, { ...elements, count, on: newOn });\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (!newMorphValue.length) {\n if (Array.isArray(elements) && elements.length === 0) {\n return;\n }\n\n remove(key);\n return;\n }\n\n if (Array.isArray(elements)) {\n set(key, newMorphValue);\n return;\n }\n\n set(key, newMorphValue[0]);\n }\n };\n\n const isMorphMutationPayload = (value: unknown): value is MorphMutationPayload => {\n return isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value);\n };\n\n const isMorphPopulateCount = (value: unknown) => value === true || value === 'true';\n\n const isMorphPopulatePayload = (value: unknown): value is MorphPopulatePayload => {\n return isObject(value) && !('__type' in value) && 'on' in value && isObject(value.on);\n };\n\n const buildAllowedMorphPopulateFragment = async (subPopulate: unknown) => {\n const newOn: Record<string, unknown> = {};\n\n for (const uid of getRegisteredContentTypeUIDs()) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n return newOn;\n };\n\n const isMorphPopulateAllOrCount = (value: unknown): value is true | 'true' | { count: true } => {\n return (\n value === true ||\n value === 'true' ||\n (isObject(value) && 'count' in value && isMorphPopulateCount(value.count) && !('on' in value))\n );\n };\n\n const isMorphCountOutput = (value: unknown): value is { count: number } => {\n return (\n isObject(value) &&\n 'count' in value &&\n typeof value.count === 'number' &&\n Object.keys(value).length === 1\n );\n };\n\n const handleMorphElements = async (elements: unknown) => {\n const allowedElements: Record<string, unknown>[] = [];\n const elementsToCheck = Array.isArray(elements) ? elements : [elements];\n\n for (const element of elementsToCheck) {\n if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n const value = (data as Record<string, unknown>)[key];\n\n if (isMorphPopulatePayload(value)) {\n await handleMorphRelation();\n return;\n }\n\n if (isMorphPopulateAllOrCount(value)) {\n const newOn = await buildAllowedMorphPopulateFragment(true);\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n set(\n key,\n value === true || value === 'true' ? { on: newOn } : { ...value, count: true, on: newOn }\n );\n return;\n }\n\n if (isMorphCountOutput(value)) {\n return;\n }\n\n await handleMorphRelation();\n return;\n }\n\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n return;\n }\n\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n\nconst getRegisteredContentTypeUIDs = () => {\n return (Object.values(strapi.contentTypes ?? {}) as Model[])\n .map((model) => model.uid)\n .filter((uid): uid is string => typeof uid === 'string');\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","isMorphMutationPayload","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","optionKey","validator","VALID_RELATION_ORDERING_KEYS","optionValue","isMorphPopulatePayload","newOn","uid","subPopulate","entries","on","scopes","map","action","isAllowed","hasAccessToSomeScopes","count","isMorphPopulateCount","newMorphValue","Array","isArray","value","isObject","buildAllowedMorphPopulateFragment","getRegisteredContentTypeUIDs","isMorphPopulateAllOrCount","isMorphCountOutput","allowedElements","elementsToCheck","element","__type","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify","values","contentTypes","model","filter"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isObject
|
|
1
|
+
import { isObject } from 'lodash';
|
|
2
2
|
import { isMorphToRelationalAttribute, constants } from '../../content-types.mjs';
|
|
3
3
|
import { VALID_RELATION_ORDERING_KEYS } from '../../relations.mjs';
|
|
4
4
|
|
|
@@ -81,13 +81,13 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
|
|
|
81
81
|
} else {
|
|
82
82
|
const newMorphValue = await handleMorphElements(elements);
|
|
83
83
|
if (!newMorphValue.length) {
|
|
84
|
-
if (isArray(elements) && elements.length === 0) {
|
|
84
|
+
if (Array.isArray(elements) && elements.length === 0) {
|
|
85
85
|
return;
|
|
86
86
|
}
|
|
87
87
|
remove(key);
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
|
-
if (isArray(elements)) {
|
|
90
|
+
if (Array.isArray(elements)) {
|
|
91
91
|
set(key, newMorphValue);
|
|
92
92
|
return;
|
|
93
93
|
}
|
|
@@ -120,7 +120,7 @@ const visitRelationAttribute = async (auth, { data, key, attribute, schema }, {
|
|
|
120
120
|
};
|
|
121
121
|
const handleMorphElements = async (elements)=>{
|
|
122
122
|
const allowedElements = [];
|
|
123
|
-
const elementsToCheck = isArray(elements) ? elements : [
|
|
123
|
+
const elementsToCheck = Array.isArray(elements) ? elements : [
|
|
124
124
|
elements
|
|
125
125
|
];
|
|
126
126
|
for (const element of elementsToCheck){
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-restricted-relations.mjs","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isArray, isObject } from 'lodash/fp';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Model, RelationOrderingOptions } from '../../types';\nimport type { Visitor } from '../../traverse/factory';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\ntype MorphElement = Record<string, unknown> & { __type: string };\ntype MorphMutationPayload = {\n connect?: unknown;\n set?: unknown;\n disconnect?: unknown;\n options?: Record<string, unknown> | null;\n};\ntype MorphPopulatePayload = {\n on: Record<string, unknown>;\n count?: unknown;\n};\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements = (data as Record<string, MorphArray | MorphElement | MorphMutationPayload>)[\n key\n ];\n\n if (!elements) {\n return;\n }\n\n if (isMorphMutationPayload(elements)) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n const options = elements.options;\n if (options && typeof options === 'object') {\n const filteredOptions: RelationOrderingOptions = {};\n\n Object.keys(options).forEach((key) => {\n const optionKey = key as keyof RelationOrderingOptions;\n const validator = VALID_RELATION_ORDERING_KEYS[optionKey];\n const optionValue = options[key];\n\n if (validator && validator(optionValue)) {\n filteredOptions[optionKey] = optionValue as RelationOrderingOptions[typeof optionKey];\n }\n });\n\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else if (isMorphPopulatePayload(elements)) {\n const newOn: Record<string, unknown> = {};\n\n for (const [uid, subPopulate] of Object.entries(elements.on)) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n const count = isMorphPopulateCount(elements.count) ? true : elements.count;\n set(key, { ...elements, count, on: newOn });\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (!newMorphValue.length) {\n if (isArray(elements) && elements.length === 0) {\n return;\n }\n\n remove(key);\n return;\n }\n\n if (isArray(elements)) {\n set(key, newMorphValue);\n return;\n }\n\n set(key, newMorphValue[0]);\n }\n };\n\n const isMorphMutationPayload = (value: unknown): value is MorphMutationPayload => {\n return isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value);\n };\n\n const isMorphPopulateCount = (value: unknown) => value === true || value === 'true';\n\n const isMorphPopulatePayload = (value: unknown): value is MorphPopulatePayload => {\n return isObject(value) && !('__type' in value) && 'on' in value && isObject(value.on);\n };\n\n const buildAllowedMorphPopulateFragment = async (subPopulate: unknown) => {\n const newOn: Record<string, unknown> = {};\n\n for (const uid of getRegisteredContentTypeUIDs()) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n return newOn;\n };\n\n const isMorphPopulateAllOrCount = (value: unknown): value is true | 'true' | { count: true } => {\n return (\n value === true ||\n value === 'true' ||\n (isObject(value) && 'count' in value && isMorphPopulateCount(value.count) && !('on' in value))\n );\n };\n\n const isMorphCountOutput = (value: unknown): value is { count: number } => {\n return (\n isObject(value) &&\n 'count' in value &&\n typeof value.count === 'number' &&\n Object.keys(value).length === 1\n );\n };\n\n const handleMorphElements = async (elements: unknown) => {\n const allowedElements: Record<string, unknown>[] = [];\n const elementsToCheck = isArray(elements) ? elements : [elements];\n\n for (const element of elementsToCheck) {\n if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n const value = (data as Record<string, unknown>)[key];\n\n if (isMorphPopulatePayload(value)) {\n await handleMorphRelation();\n return;\n }\n\n if (isMorphPopulateAllOrCount(value)) {\n const newOn = await buildAllowedMorphPopulateFragment(true);\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n set(\n key,\n value === true || value === 'true' ? { on: newOn } : { ...value, count: true, on: newOn }\n );\n return;\n }\n\n if (isMorphCountOutput(value)) {\n return;\n }\n\n await handleMorphRelation();\n return;\n }\n\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n return;\n }\n\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n\nconst getRegisteredContentTypeUIDs = () => {\n return (Object.values(strapi.contentTypes ?? {}) as Model[])\n .map((model) => model.uid)\n .filter((uid): uid is string => typeof uid === 'string');\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","isMorphMutationPayload","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","optionKey","validator","VALID_RELATION_ORDERING_KEYS","optionValue","isMorphPopulatePayload","newOn","uid","subPopulate","entries","on","scopes","map","action","isAllowed","hasAccessToSomeScopes","count","isMorphPopulateCount","newMorphValue","isArray","value","isObject","buildAllowedMorphPopulateFragment","getRegisteredContentTypeUIDs","isMorphPopulateAllOrCount","isMorphCountOutput","allowedElements","elementsToCheck","element","__type","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify","values","contentTypes","model","filter"],"mappings":";;;;AAMA,MAAMA,iBAAAA,GAAoB;AAAC,IAAA;AAAO,CAAA;AAClC,MAAM,EAAEC,oBAAoB,EAAEC,oBAAoB,EAAE,GAAGC,SAA0B;AAejF,gCAAe,CAAA,CAACC,IAAAA,GACd,CAACC,cAAAA,EAAgBC,YAAAA,GAAAA;QACf,MAAM,EAAEC,SAAS,EAAE,GAAGF,cAAAA;;;;;;AAOtB,QAAA,IAAI,CAACE,SAAAA,IAAaA,SAAAA,CAAUC,IAAI,KAAK,UAAA,EAAY;AAC/C,YAAA;AACF,QAAA;QAEA,OAAOC,sBAAAA,CAAuBL,MAAMC,cAAAA,EAAgBC,YAAAA,CAAAA;AACtD,IAAA,CAAA;AAEF,MAAMG,yBAAyB,OAC7BL,IAAAA,EACA,EAAEM,IAAI,EAAEC,GAAG,EAAEJ,SAAS,EAAEK,MAAM,EAA0B,EACxD,EAAEC,MAAM,EAAEC,GAAG,EAA0B,GAAA;;AAGvC,IAAA,IAAI,CAACP,SAAAA,EAAW;AACd,QAAA;AACF,IAAA;AAEA,IAAA,MAAMQ,mBAAAA,GAAsB,UAAA;AAC1B,QAAA,MAAMC,QAAAA,GAAYN,IAAyE,CACzFC,GAAAA,CACD;AAED,QAAA,IAAI,CAACK,QAAAA,EAAU;AACb,YAAA;AACF,QAAA;AAEA,QAAA,IAAIC,uBAAuBD,QAAAA,CAAAA,EAAW;AACpC,YAAA,MAAME,WAAoC,EAAC;AAE3C,YAAA,MAAMC,UAAU,MAAMC,mBAAAA,CAAoBJ,QAAAA,CAASG,OAAO,IAAI,EAAE,CAAA;AAChE,YAAA,MAAME,SAAS,MAAMD,mBAAAA,CAAoBJ,QAAAA,CAASF,GAAG,IAAI,EAAE,CAAA;AAC3D,YAAA,MAAMQ,aAAa,MAAMF,mBAAAA,CAAoBJ,QAAAA,CAASM,UAAU,IAAI,EAAE,CAAA;YAEtE,IAAIH,OAAAA,CAAQI,MAAM,GAAG,CAAA,EAAG;AACtBL,gBAAAA,QAAAA,CAASC,OAAO,GAAGA,OAAAA;AACrB,YAAA;YAEA,IAAIE,MAAAA,CAAOE,MAAM,GAAG,CAAA,EAAG;AACrBL,gBAAAA,QAAAA,CAASJ,GAAG,GAAGO,MAAAA;AACjB,YAAA;YAEA,IAAIC,UAAAA,CAAWC,MAAM,GAAG,CAAA,EAAG;AACzBL,gBAAAA,QAAAA,CAASI,UAAU,GAAGA,UAAAA;AACxB,YAAA;;YAGA,MAAME,OAAAA,GAAUR,SAASQ,OAAO;YAChC,IAAIA,OAAAA,IAAW,OAAOA,OAAAA,KAAY,QAAA,EAAU;AAC1C,gBAAA,MAAMC,kBAA2C,EAAC;AAElDC,gBAAAA,MAAAA,CAAOC,IAAI,CAACH,OAAAA,CAAAA,CAASI,OAAO,CAAC,CAACjB,GAAAA,GAAAA;AAC5B,oBAAA,MAAMkB,SAAAA,GAAYlB,GAAAA;oBAClB,MAAMmB,SAAAA,GAAYC,4BAA4B,CAACF,SAAAA,CAAU;oBACzD,MAAMG,WAAAA,GAAcR,OAAO,CAACb,GAAAA,CAAI;oBAEhC,IAAImB,SAAAA,IAAaA,UAAUE,WAAAA,CAAAA,EAAc;wBACvCP,eAAe,CAACI,UAAU,GAAGG,WAAAA;AAC/B,oBAAA;AACF,gBAAA,CAAA,CAAA;AAEAd,gBAAAA,QAAAA,CAASM,OAAO,GAAGC,eAAAA;YACrB,CAAA,MAAO;gBACLP,QAAAA,CAASM,OAAO,GAAG,EAAC;AACtB,YAAA;AAEAV,YAAAA,GAAAA,CAAIH,GAAAA,EAAKO,QAAAA,CAAAA;QACX,CAAA,MAAO,IAAIe,uBAAuBjB,QAAAA,CAAAA,EAAW;AAC3C,YAAA,MAAMkB,QAAiC,EAAC;YAExC,KAAK,MAAM,CAACC,GAAAA,EAAKC,WAAAA,CAAY,IAAIV,OAAOW,OAAO,CAACrB,QAAAA,CAASsB,EAAE,CAAA,CAAG;gBAC5D,MAAMC,MAAAA,GAASvC,iBAAAA,CAAkBwC,GAAG,CAAC,CAACC,SAAW,CAAA,EAAGN,GAAAA,CAAI,CAAC,EAAEM,MAAAA,CAAAA,CAAQ,CAAA;gBACnE,MAAMC,SAAAA,GAAY,MAAMC,qBAAAA,CAAsBJ,MAAAA,EAAQnC,IAAAA,CAAAA;AAEtD,gBAAA,IAAIsC,SAAAA,EAAW;oBACbR,KAAK,CAACC,IAAI,GAAGC,WAAAA;AACf,gBAAA;AACF,YAAA;AAEA,YAAA,IAAIV,OAAOC,IAAI,CAACO,KAAAA,CAAAA,CAAOX,MAAM,KAAK,CAAA,EAAG;gBACnCV,MAAAA,CAAOF,GAAAA,CAAAA;AACP,gBAAA;AACF,YAAA;AAEA,YAAA,MAAMiC,QAAQC,oBAAAA,CAAqB7B,QAAAA,CAAS4B,KAAK,CAAA,GAAI,IAAA,GAAO5B,SAAS4B,KAAK;AAC1E9B,YAAAA,GAAAA,CAAIH,GAAAA,EAAK;AAAE,gBAAA,GAAGK,QAAQ;AAAE4B,gBAAAA,KAAAA;gBAAON,EAAAA,EAAIJ;AAAM,aAAA,CAAA;QAC3C,CAAA,MAAO;YACL,MAAMY,aAAAA,GAAgB,MAAM1B,mBAAAA,CAAoBJ,QAAAA,CAAAA;YAEhD,IAAI,CAAC8B,aAAAA,CAAcvB,MAAM,EAAE;AACzB,gBAAA,IAAIwB,OAAAA,CAAQ/B,QAAAA,CAAAA,IAAaA,QAAAA,CAASO,MAAM,KAAK,CAAA,EAAG;AAC9C,oBAAA;AACF,gBAAA;gBAEAV,MAAAA,CAAOF,GAAAA,CAAAA;AACP,gBAAA;AACF,YAAA;AAEA,YAAA,IAAIoC,QAAQ/B,QAAAA,CAAAA,EAAW;AACrBF,gBAAAA,GAAAA,CAAIH,GAAAA,EAAKmC,aAAAA,CAAAA;AACT,gBAAA;AACF,YAAA;YAEAhC,GAAAA,CAAIH,GAAAA,EAAKmC,aAAa,CAAC,CAAA,CAAE,CAAA;AAC3B,QAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAM7B,yBAAyB,CAAC+B,KAAAA,GAAAA;QAC9B,OAAOC,QAAAA,CAASD,WAAW,SAAA,IAAaA,SAAS,KAAA,IAASA,KAAAA,IAAS,gBAAgBA,KAAI,CAAA;AACzF,IAAA,CAAA;AAEA,IAAA,MAAMH,oBAAAA,GAAuB,CAACG,KAAAA,GAAmBA,KAAAA,KAAU,QAAQA,KAAAA,KAAU,MAAA;AAE7E,IAAA,MAAMf,yBAAyB,CAACe,KAAAA,GAAAA;QAC9B,OAAOC,QAAAA,CAASD,KAAAA,CAAAA,IAAU,EAAE,QAAA,IAAYA,KAAI,CAAA,IAAM,IAAA,IAAQA,KAAAA,IAASC,QAAAA,CAASD,KAAAA,CAAMV,EAAE,CAAA;AACtF,IAAA,CAAA;AAEA,IAAA,MAAMY,oCAAoC,OAAOd,WAAAA,GAAAA;AAC/C,QAAA,MAAMF,QAAiC,EAAC;QAExC,KAAK,MAAMC,OAAOgB,4BAAAA,EAAAA,CAAgC;YAChD,MAAMZ,MAAAA,GAASvC,iBAAAA,CAAkBwC,GAAG,CAAC,CAACC,SAAW,CAAA,EAAGN,GAAAA,CAAI,CAAC,EAAEM,MAAAA,CAAAA,CAAQ,CAAA;YACnE,MAAMC,SAAAA,GAAY,MAAMC,qBAAAA,CAAsBJ,MAAAA,EAAQnC,IAAAA,CAAAA;AAEtD,YAAA,IAAIsC,SAAAA,EAAW;gBACbR,KAAK,CAACC,IAAI,GAAGC,WAAAA;AACf,YAAA;AACF,QAAA;QAEA,OAAOF,KAAAA;AACT,IAAA,CAAA;AAEA,IAAA,MAAMkB,4BAA4B,CAACJ,KAAAA,GAAAA;AACjC,QAAA,OACEA,KAAAA,KAAU,IAAA,IACVA,KAAAA,KAAU,MAAA,IACTC,SAASD,KAAAA,CAAAA,IAAU,OAAA,IAAWA,KAAAA,IAASH,oBAAAA,CAAqBG,MAAMJ,KAAK,CAAA,IAAK,EAAE,QAAQI,KAAI,CAAA;AAE/F,IAAA,CAAA;AAEA,IAAA,MAAMK,qBAAqB,CAACL,KAAAA,GAAAA;AAC1B,QAAA,OACEC,QAAAA,CAASD,KAAAA,CAAAA,IACT,OAAA,IAAWA,KAAAA,IACX,OAAOA,KAAAA,CAAMJ,KAAK,KAAK,QAAA,IACvBlB,MAAAA,CAAOC,IAAI,CAACqB,KAAAA,CAAAA,CAAOzB,MAAM,KAAK,CAAA;AAElC,IAAA,CAAA;AAEA,IAAA,MAAMH,sBAAsB,OAAOJ,QAAAA,GAAAA;AACjC,QAAA,MAAMsC,kBAA6C,EAAE;QACrD,MAAMC,eAAAA,GAAkBR,OAAAA,CAAQ/B,QAAAA,CAAAA,GAAYA,QAAAA,GAAW;AAACA,YAAAA;AAAS,SAAA;QAEjE,KAAK,MAAMwC,WAAWD,eAAAA,CAAiB;AACrC,YAAA,IAAI,CAACN,QAAAA,CAASO,OAAAA,CAAAA,IAAY,EAAE,QAAA,IAAYA,OAAM,CAAA,IAAM,OAAOA,OAAAA,CAAQC,MAAM,KAAK,QAAA,EAAU;AACtF,gBAAA;AACF,YAAA;AAEA,YAAA,MAAMlB,MAAAA,GAASvC,iBAAAA,CAAkBwC,GAAG,CAAC,CAACC,MAAAA,GAAW,CAAA,EAAGe,OAAAA,CAAQC,MAAM,CAAC,CAAC,EAAEhB,MAAAA,CAAAA,CAAQ,CAAA;YAC9E,MAAMC,SAAAA,GAAY,MAAMC,qBAAAA,CAAsBJ,MAAAA,EAAQnC,IAAAA,CAAAA;AAEtD,YAAA,IAAIsC,SAAAA,EAAW;AACbY,gBAAAA,eAAAA,CAAgBI,IAAI,CAACF,OAAAA,CAAAA;AACvB,YAAA;AACF,QAAA;QAEA,OAAOF,eAAAA;AACT,IAAA,CAAA;AAEA,IAAA,MAAMK,qBAAAA,GAAwB,UAAA;AAC5B,QAAA,MAAMpB,MAAAA,GAASvC,iBAAAA,CAAkBwC,GAAG,CAAC,CAACC,MAAAA,GAAW,CAAA,EAAGlC,SAAAA,CAAUqD,MAAM,CAAC,CAAC,EAAEnB,MAAAA,CAAAA,CAAQ,CAAA;QAChF,MAAMC,SAAAA,GAAY,MAAMC,qBAAAA,CAAsBJ,MAAAA,EAAQnC,IAAAA,CAAAA;AAEtD,QAAA,IAAI,CAACsC,SAAAA,EAAW;YACd7B,MAAAA,CAAOF,GAAAA,CAAAA;AACT,QAAA;AACF,IAAA,CAAA;AAEA,IAAA,MAAMkD,iBAAAA,GAAoB;AAAC5D,QAAAA,oBAAAA;AAAsBC,QAAAA;AAAqB,KAAA,CAAC4D,QAAQ,CAACnD,GAAAA,CAAAA;IAEhF,IAAIR,4BAA6C,CAACI,SAAAA,CAAAA,EAAY;AAC5D,QAAA,MAAMyC,KAAAA,GAAStC,IAAgC,CAACC,GAAAA,CAAI;AAEpD,QAAA,IAAIsB,uBAAuBe,KAAAA,CAAAA,EAAQ;YACjC,MAAMjC,mBAAAA,EAAAA;AACN,YAAA;AACF,QAAA;AAEA,QAAA,IAAIqC,0BAA0BJ,KAAAA,CAAAA,EAAQ;YACpC,MAAMd,KAAAA,GAAQ,MAAMgB,iCAAAA,CAAkC,IAAA,CAAA;AAEtD,YAAA,IAAIxB,OAAOC,IAAI,CAACO,KAAAA,CAAAA,CAAOX,MAAM,KAAK,CAAA,EAAG;gBACnCV,MAAAA,CAAOF,GAAAA,CAAAA;AACP,gBAAA;AACF,YAAA;AAEAG,YAAAA,GAAAA,CACEH,GAAAA,EACAqC,KAAAA,KAAU,IAAA,IAAQA,KAAAA,KAAU,MAAA,GAAS;gBAAEV,EAAAA,EAAIJ;aAAM,GAAI;AAAE,gBAAA,GAAGc,KAAK;gBAAEJ,KAAAA,EAAO,IAAA;gBAAMN,EAAAA,EAAIJ;AAAM,aAAA,CAAA;AAE1F,YAAA;AACF,QAAA;AAEA,QAAA,IAAImB,mBAAmBL,KAAAA,CAAAA,EAAQ;AAC7B,YAAA;AACF,QAAA;QAEA,MAAMjC,mBAAAA,EAAAA;AACN,QAAA;AACF,IAAA;AAEA,IAAA,IAAI8C,iBAAAA,IAAqBjD,MAAAA,CAAOY,OAAO,EAAEuC,qBAAAA,EAAuB;AAC9D,QAAA;AACF,IAAA;IAEA,MAAMJ,qBAAAA,EAAAA;AACR,CAAA;AAEA;;;;;;;;;;IAWA,MAAMK,qBAAqB,IAAIC,OAAAA,EAAAA;AAE/B,MAAMC,cAAAA,GAAiB,OAAOC,KAAAA,EAAe/D,IAAAA,GAAAA;AAC3C,IAAA,MAAMgE,SAAAA,GAAY,OAAOhE,IAAAA,KAAS,QAAA,IAAYA,IAAAA,KAAS,IAAA;IAEvD,IAAIiE,SAAAA;AAEJ,IAAA,IAAID,SAAAA,EAAW;QACbC,SAAAA,GAAYL,kBAAAA,CAAmBM,GAAG,CAAClE,IAAAA,CAAAA;AAEnC,QAAA,IAAIiE,cAAcE,SAAAA,EAAW;AAC3BF,YAAAA,SAAAA,GAAY,IAAIG,GAAAA,EAAAA;YAChBR,kBAAAA,CAAmBlD,GAAG,CAACV,IAAAA,EAAgBiE,SAAAA,CAAAA;QACzC,CAAA,MAAO;YACL,MAAMI,MAAAA,GAASJ,SAAAA,CAAUC,GAAG,CAACH,KAAAA,CAAAA;AAC7B,YAAA,IAAIM,WAAWF,SAAAA,EAAW;gBACxB,OAAOE,MAAAA;AACT,YAAA;AACF,QAAA;AACF,IAAA;IAEA,IAAIC,OAAAA;IACJ,IAAI;AACF,QAAA,MAAMC,MAAAA,CAAOvE,IAAI,CAACwE,MAAM,CAACxE,IAAAA,EAAM;AAAE+D,YAAAA;AAAM,SAAA,CAAA;QACvCO,OAAAA,GAAU,IAAA;AACZ,IAAA,CAAA,CAAE,OAAM;QACNA,OAAAA,GAAU,KAAA;AACZ,IAAA;AAEAL,IAAAA,SAAAA,EAAWvD,IAAIqD,KAAAA,EAAOO,OAAAA,CAAAA;IAEtB,OAAOA,OAAAA;AACT,CAAA;AAEA,MAAM/B,qBAAAA,GAAwB,OAAOJ,MAAAA,EAAkBnC,IAAAA,GAAAA;IACrD,KAAK,MAAM+D,SAAS5B,MAAAA,CAAQ;QAC1B,IAAI,MAAM2B,cAAAA,CAAeC,KAAAA,EAAO/D,IAAAA,CAAAA,EAAO;YACrC,OAAO,IAAA;AACT,QAAA;AACF,IAAA;IAEA,OAAO,KAAA;AACT,CAAA;AAEA,MAAM+C,4BAAAA,GAA+B,IAAA;IACnC,OAAQzB,OAAOmD,MAAM,CAACF,OAAOG,YAAY,IAAI,EAAC,CAAA,CAC3CtC,GAAG,CAAC,CAACuC,KAAAA,GAAUA,MAAM5C,GAAG,CAAA,CACxB6C,MAAM,CAAC,CAAC7C,GAAAA,GAAuB,OAAOA,GAAAA,KAAQ,QAAA,CAAA;AACnD,CAAA;;;;"}
|
|
1
|
+
{"version":3,"file":"remove-restricted-relations.mjs","sources":["../../../src/sanitize/visitors/remove-restricted-relations.ts"],"sourcesContent":["import { isObject } from 'lodash';\nimport * as contentTypeUtils from '../../content-types';\nimport type { Model, RelationOrderingOptions } from '../../types';\nimport type { Visitor } from '../../traverse/factory';\nimport { VALID_RELATION_ORDERING_KEYS } from '../../relations';\n\nconst ACTIONS_TO_VERIFY = ['find'];\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypeUtils.constants;\n\ntype MorphArray = Array<{ __type: string }>;\ntype MorphElement = Record<string, unknown> & { __type: string };\ntype MorphMutationPayload = {\n connect?: unknown;\n set?: unknown;\n disconnect?: unknown;\n options?: Record<string, unknown> | null;\n};\ntype MorphPopulatePayload = {\n on: Record<string, unknown>;\n count?: unknown;\n};\n\nexport default (auth: unknown): Visitor =>\n (visitorOptions, visitorUtils) => {\n const { attribute } = visitorOptions;\n\n // Deliberately not an `async` function. This visitor is invoked for every key of\n // every node of every entity, and only relation attributes need any work at all.\n // Declaring it `async` meant each of those keys allocated a promise purely to\n // resolve it immediately, and the traversal then awaited it. Returning early\n // without a promise lets the traversal stay synchronous for the common case.\n if (!attribute || attribute.type !== 'relation') {\n return;\n }\n\n return visitRelationAttribute(auth, visitorOptions, visitorUtils) as unknown as void;\n };\n\nconst visitRelationAttribute = async (\n auth: unknown,\n { data, key, attribute, schema }: Parameters<Visitor>[0],\n { remove, set }: Parameters<Visitor>[1]\n) => {\n // Narrowing only; the caller reaches this for relation attributes exclusively.\n if (!attribute) {\n return;\n }\n\n const handleMorphRelation = async () => {\n const elements = (data as Record<string, MorphArray | MorphElement | MorphMutationPayload>)[\n key\n ];\n\n if (!elements) {\n return;\n }\n\n if (isMorphMutationPayload(elements)) {\n const newValue: Record<string, unknown> = {};\n\n const connect = await handleMorphElements(elements.connect || []);\n const relSet = await handleMorphElements(elements.set || []);\n const disconnect = await handleMorphElements(elements.disconnect || []);\n\n if (connect.length > 0) {\n newValue.connect = connect;\n }\n\n if (relSet.length > 0) {\n newValue.set = relSet;\n }\n\n if (disconnect.length > 0) {\n newValue.disconnect = disconnect;\n }\n\n // TODO: this should technically be in its own visitor to check morph options, but for now we'll handle it here\n const options = elements.options;\n if (options && typeof options === 'object') {\n const filteredOptions: RelationOrderingOptions = {};\n\n Object.keys(options).forEach((key) => {\n const optionKey = key as keyof RelationOrderingOptions;\n const validator = VALID_RELATION_ORDERING_KEYS[optionKey];\n const optionValue = options[key];\n\n if (validator && validator(optionValue)) {\n filteredOptions[optionKey] = optionValue as RelationOrderingOptions[typeof optionKey];\n }\n });\n\n newValue.options = filteredOptions;\n } else {\n newValue.options = {};\n }\n\n set(key, newValue);\n } else if (isMorphPopulatePayload(elements)) {\n const newOn: Record<string, unknown> = {};\n\n for (const [uid, subPopulate] of Object.entries(elements.on)) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n const count = isMorphPopulateCount(elements.count) ? true : elements.count;\n set(key, { ...elements, count, on: newOn });\n } else {\n const newMorphValue = await handleMorphElements(elements);\n\n if (!newMorphValue.length) {\n if (Array.isArray(elements) && elements.length === 0) {\n return;\n }\n\n remove(key);\n return;\n }\n\n if (Array.isArray(elements)) {\n set(key, newMorphValue);\n return;\n }\n\n set(key, newMorphValue[0]);\n }\n };\n\n const isMorphMutationPayload = (value: unknown): value is MorphMutationPayload => {\n return isObject(value) && ('connect' in value || 'set' in value || 'disconnect' in value);\n };\n\n const isMorphPopulateCount = (value: unknown) => value === true || value === 'true';\n\n const isMorphPopulatePayload = (value: unknown): value is MorphPopulatePayload => {\n return isObject(value) && !('__type' in value) && 'on' in value && isObject(value.on);\n };\n\n const buildAllowedMorphPopulateFragment = async (subPopulate: unknown) => {\n const newOn: Record<string, unknown> = {};\n\n for (const uid of getRegisteredContentTypeUIDs()) {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${uid}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n newOn[uid] = subPopulate;\n }\n }\n\n return newOn;\n };\n\n const isMorphPopulateAllOrCount = (value: unknown): value is true | 'true' | { count: true } => {\n return (\n value === true ||\n value === 'true' ||\n (isObject(value) && 'count' in value && isMorphPopulateCount(value.count) && !('on' in value))\n );\n };\n\n const isMorphCountOutput = (value: unknown): value is { count: number } => {\n return (\n isObject(value) &&\n 'count' in value &&\n typeof value.count === 'number' &&\n Object.keys(value).length === 1\n );\n };\n\n const handleMorphElements = async (elements: unknown) => {\n const allowedElements: Record<string, unknown>[] = [];\n const elementsToCheck = Array.isArray(elements) ? elements : [elements];\n\n for (const element of elementsToCheck) {\n if (!isObject(element) || !('__type' in element) || typeof element.__type !== 'string') {\n continue;\n }\n\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${element.__type}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (isAllowed) {\n allowedElements.push(element);\n }\n }\n\n return allowedElements;\n };\n\n const handleRegularRelation = async () => {\n const scopes = ACTIONS_TO_VERIFY.map((action) => `${attribute.target}.${action}`);\n const isAllowed = await hasAccessToSomeScopes(scopes, auth);\n\n if (!isAllowed) {\n remove(key);\n }\n };\n\n const isCreatorRelation = [CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE].includes(key);\n\n if (contentTypeUtils.isMorphToRelationalAttribute(attribute)) {\n const value = (data as Record<string, unknown>)[key];\n\n if (isMorphPopulatePayload(value)) {\n await handleMorphRelation();\n return;\n }\n\n if (isMorphPopulateAllOrCount(value)) {\n const newOn = await buildAllowedMorphPopulateFragment(true);\n\n if (Object.keys(newOn).length === 0) {\n remove(key);\n return;\n }\n\n set(\n key,\n value === true || value === 'true' ? { on: newOn } : { ...value, count: true, on: newOn }\n );\n return;\n }\n\n if (isMorphCountOutput(value)) {\n return;\n }\n\n await handleMorphRelation();\n return;\n }\n\n if (isCreatorRelation && schema.options?.populateCreatorFields) {\n return;\n }\n\n await handleRegularRelation();\n};\n\n/**\n * Per-`auth` memo of scope decisions.\n *\n * The decision for a given scope depends only on the ability carried by `auth`, which is\n * built once per request and not mutated while the response is sanitized. Sanitizing a\n * list re-asks the same question for every entity, so a 25-entity page with four\n * relations asked ~100 identical questions; each denial also constructed an Error, and\n * capturing a stack trace is by far the most expensive part of that.\n *\n * Keyed weakly on the `auth` object so entries are collected with the request.\n */\nconst scopeDecisionCache = new WeakMap<object, Map<string, boolean>>();\n\nconst canAccessScope = async (scope: string, auth: unknown): Promise<boolean> => {\n const cacheable = typeof auth === 'object' && auth !== null;\n\n let decisions: Map<string, boolean> | undefined;\n\n if (cacheable) {\n decisions = scopeDecisionCache.get(auth as object);\n\n if (decisions === undefined) {\n decisions = new Map();\n scopeDecisionCache.set(auth as object, decisions);\n } else {\n const cached = decisions.get(scope);\n if (cached !== undefined) {\n return cached;\n }\n }\n }\n\n let allowed: boolean;\n try {\n await strapi.auth.verify(auth, { scope });\n allowed = true;\n } catch {\n allowed = false;\n }\n\n decisions?.set(scope, allowed);\n\n return allowed;\n};\n\nconst hasAccessToSomeScopes = async (scopes: string[], auth: unknown) => {\n for (const scope of scopes) {\n if (await canAccessScope(scope, auth)) {\n return true;\n }\n }\n\n return false;\n};\n\nconst getRegisteredContentTypeUIDs = () => {\n return (Object.values(strapi.contentTypes ?? {}) as Model[])\n .map((model) => model.uid)\n .filter((uid): uid is string => typeof uid === 'string');\n};\n"],"names":["ACTIONS_TO_VERIFY","CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypeUtils","auth","visitorOptions","visitorUtils","attribute","type","visitRelationAttribute","data","key","schema","remove","set","handleMorphRelation","elements","isMorphMutationPayload","newValue","connect","handleMorphElements","relSet","disconnect","length","options","filteredOptions","Object","keys","forEach","optionKey","validator","VALID_RELATION_ORDERING_KEYS","optionValue","isMorphPopulatePayload","newOn","uid","subPopulate","entries","on","scopes","map","action","isAllowed","hasAccessToSomeScopes","count","isMorphPopulateCount","newMorphValue","Array","isArray","value","isObject","buildAllowedMorphPopulateFragment","getRegisteredContentTypeUIDs","isMorphPopulateAllOrCount","isMorphCountOutput","allowedElements","elementsToCheck","element","__type","push","handleRegularRelation","target","isCreatorRelation","includes","populateCreatorFields","scopeDecisionCache","WeakMap","canAccessScope","scope","cacheable","decisions","get","undefined","Map","cached","allowed","strapi","verify","values","contentTypes","model","filter"],"mappings":"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"}
|
package/dist/security.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY;;;;CAIf,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GACxC,aAAa,CAAC,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,EACzD,YAAY;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE;WADV,MAAM;aAAW,GAAG;
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY;;;;CAIf,CAAC;AAEX;;;;;;GAMG;AACH,eAAO,MAAM,6BAA6B,GACxC,aAAa,CAAC,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE,CAAC,EAAE,EACzD,YAAY;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,GAAG,CAAA;CAAE;WADV,MAAM;aAAW,GAAG;IAqBrD,CAAC"}
|
package/dist/security.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var _ = require('lodash');
|
|
4
4
|
|
|
5
5
|
const CSP_DEFAULTS = {
|
|
6
6
|
'connect-src': [
|
|
@@ -33,12 +33,12 @@ const CSP_DEFAULTS = {
|
|
|
33
33
|
}
|
|
34
34
|
if (typeof currentMiddleware === 'object' && currentMiddleware.name === middleware.name) {
|
|
35
35
|
// Deep merge (+ concat arrays) the new config with the current middleware config
|
|
36
|
-
return
|
|
36
|
+
return _.mergeWith({}, currentMiddleware, middleware, (objValue, srcValue)=>{
|
|
37
37
|
if (Array.isArray(objValue)) {
|
|
38
38
|
return Array.from(new Set(objValue.concat(srcValue)));
|
|
39
39
|
}
|
|
40
40
|
return undefined;
|
|
41
|
-
}
|
|
41
|
+
});
|
|
42
42
|
}
|
|
43
43
|
return currentMiddleware;
|
|
44
44
|
});
|
package/dist/security.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sources":["../src/security.ts"],"sourcesContent":["import { mergeWith } from 'lodash
|
|
1
|
+
{"version":3,"file":"security.js","sources":["../src/security.ts"],"sourcesContent":["import { mergeWith } from 'lodash';\n\nexport const CSP_DEFAULTS = {\n 'connect-src': [\"'self'\", 'https:'],\n 'img-src': [\"'self'\", 'data:', 'blob:', 'https://market-assets.strapi.io'],\n 'media-src': [\"'self'\", 'data:', 'blob:'],\n} as const;\n\n/**\n * Utility to extend Strapi middleware configuration. Mainly used to extend the CSP directives from the security middleware.\n *\n * @param middlewares - Array of middleware configurations\n * @param middleware - Middleware configuration to merge/add\n * @returns Modified middlewares array with the new configuration merged\n */\nexport const extendMiddlewareConfiguration = (\n middlewares: (string | { name?: string; config?: any })[],\n middleware: { name: string; config?: any }\n) => {\n return middlewares.map((currentMiddleware) => {\n if (typeof currentMiddleware === 'string' && currentMiddleware === middleware.name) {\n // Use the new config object if the middleware has no config property yet\n return middleware;\n }\n\n if (typeof currentMiddleware === 'object' && currentMiddleware.name === middleware.name) {\n // Deep merge (+ concat arrays) the new config with the current middleware config\n return mergeWith({}, currentMiddleware, middleware, (objValue, srcValue) => {\n if (Array.isArray(objValue)) {\n return Array.from(new Set(objValue.concat(srcValue)));\n }\n return undefined;\n });\n }\n\n return currentMiddleware;\n });\n};\n"],"names":["CSP_DEFAULTS","extendMiddlewareConfiguration","middlewares","middleware","map","currentMiddleware","name","mergeWith","objValue","srcValue","Array","isArray","from","Set","concat","undefined"],"mappings":";;;;MAEaA,YAAAA,GAAe;IAC1B,aAAA,EAAe;AAAC,QAAA,QAAA;AAAU,QAAA;AAAS,KAAA;IACnC,SAAA,EAAW;AAAC,QAAA,QAAA;AAAU,QAAA,OAAA;AAAS,QAAA,OAAA;AAAS,QAAA;AAAkC,KAAA;IAC1E,WAAA,EAAa;AAAC,QAAA,QAAA;AAAU,QAAA,OAAA;AAAS,QAAA;AAAQ;AAC3C;AAEA;;;;;;AAMC,IACM,MAAMC,6BAAAA,GAAgC,CAC3CC,WAAAA,EACAC,UAAAA,GAAAA;IAEA,OAAOD,WAAAA,CAAYE,GAAG,CAAC,CAACC,iBAAAA,GAAAA;AACtB,QAAA,IAAI,OAAOA,iBAAAA,KAAsB,QAAA,IAAYA,iBAAAA,KAAsBF,UAAAA,CAAWG,IAAI,EAAE;;YAElF,OAAOH,UAAAA;AACT,QAAA;QAEA,IAAI,OAAOE,sBAAsB,QAAA,IAAYA,iBAAAA,CAAkBC,IAAI,KAAKH,UAAAA,CAAWG,IAAI,EAAE;;AAEvF,YAAA,OAAOC,YAAU,EAAC,EAAGF,iBAAAA,EAAmBF,UAAAA,EAAY,CAACK,QAAAA,EAAUC,QAAAA,GAAAA;gBAC7D,IAAIC,KAAAA,CAAMC,OAAO,CAACH,QAAAA,CAAAA,EAAW;AAC3B,oBAAA,OAAOE,MAAME,IAAI,CAAC,IAAIC,GAAAA,CAAIL,QAAAA,CAASM,MAAM,CAACL,QAAAA,CAAAA,CAAAA,CAAAA;AAC5C,gBAAA;gBACA,OAAOM,SAAAA;AACT,YAAA,CAAA,CAAA;AACF,QAAA;QAEA,OAAOV,iBAAAA;AACT,IAAA,CAAA,CAAA;AACF;;;;;"}
|
package/dist/security.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mergeWith } from 'lodash
|
|
1
|
+
import { mergeWith } from 'lodash';
|
|
2
2
|
|
|
3
3
|
const CSP_DEFAULTS = {
|
|
4
4
|
'connect-src': [
|
|
@@ -31,12 +31,12 @@ const CSP_DEFAULTS = {
|
|
|
31
31
|
}
|
|
32
32
|
if (typeof currentMiddleware === 'object' && currentMiddleware.name === middleware.name) {
|
|
33
33
|
// Deep merge (+ concat arrays) the new config with the current middleware config
|
|
34
|
-
return mergeWith((objValue, srcValue)=>{
|
|
34
|
+
return mergeWith({}, currentMiddleware, middleware, (objValue, srcValue)=>{
|
|
35
35
|
if (Array.isArray(objValue)) {
|
|
36
36
|
return Array.from(new Set(objValue.concat(srcValue)));
|
|
37
37
|
}
|
|
38
38
|
return undefined;
|
|
39
|
-
}
|
|
39
|
+
});
|
|
40
40
|
}
|
|
41
41
|
return currentMiddleware;
|
|
42
42
|
});
|
package/dist/security.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.mjs","sources":["../src/security.ts"],"sourcesContent":["import { mergeWith } from 'lodash
|
|
1
|
+
{"version":3,"file":"security.mjs","sources":["../src/security.ts"],"sourcesContent":["import { mergeWith } from 'lodash';\n\nexport const CSP_DEFAULTS = {\n 'connect-src': [\"'self'\", 'https:'],\n 'img-src': [\"'self'\", 'data:', 'blob:', 'https://market-assets.strapi.io'],\n 'media-src': [\"'self'\", 'data:', 'blob:'],\n} as const;\n\n/**\n * Utility to extend Strapi middleware configuration. Mainly used to extend the CSP directives from the security middleware.\n *\n * @param middlewares - Array of middleware configurations\n * @param middleware - Middleware configuration to merge/add\n * @returns Modified middlewares array with the new configuration merged\n */\nexport const extendMiddlewareConfiguration = (\n middlewares: (string | { name?: string; config?: any })[],\n middleware: { name: string; config?: any }\n) => {\n return middlewares.map((currentMiddleware) => {\n if (typeof currentMiddleware === 'string' && currentMiddleware === middleware.name) {\n // Use the new config object if the middleware has no config property yet\n return middleware;\n }\n\n if (typeof currentMiddleware === 'object' && currentMiddleware.name === middleware.name) {\n // Deep merge (+ concat arrays) the new config with the current middleware config\n return mergeWith({}, currentMiddleware, middleware, (objValue, srcValue) => {\n if (Array.isArray(objValue)) {\n return Array.from(new Set(objValue.concat(srcValue)));\n }\n return undefined;\n });\n }\n\n return currentMiddleware;\n });\n};\n"],"names":["CSP_DEFAULTS","extendMiddlewareConfiguration","middlewares","middleware","map","currentMiddleware","name","mergeWith","objValue","srcValue","Array","isArray","from","Set","concat","undefined"],"mappings":";;MAEaA,YAAAA,GAAe;IAC1B,aAAA,EAAe;AAAC,QAAA,QAAA;AAAU,QAAA;AAAS,KAAA;IACnC,SAAA,EAAW;AAAC,QAAA,QAAA;AAAU,QAAA,OAAA;AAAS,QAAA,OAAA;AAAS,QAAA;AAAkC,KAAA;IAC1E,WAAA,EAAa;AAAC,QAAA,QAAA;AAAU,QAAA,OAAA;AAAS,QAAA;AAAQ;AAC3C;AAEA;;;;;;AAMC,IACM,MAAMC,6BAAAA,GAAgC,CAC3CC,WAAAA,EACAC,UAAAA,GAAAA;IAEA,OAAOD,WAAAA,CAAYE,GAAG,CAAC,CAACC,iBAAAA,GAAAA;AACtB,QAAA,IAAI,OAAOA,iBAAAA,KAAsB,QAAA,IAAYA,iBAAAA,KAAsBF,UAAAA,CAAWG,IAAI,EAAE;;YAElF,OAAOH,UAAAA;AACT,QAAA;QAEA,IAAI,OAAOE,sBAAsB,QAAA,IAAYA,iBAAAA,CAAkBC,IAAI,KAAKH,UAAAA,CAAWG,IAAI,EAAE;;AAEvF,YAAA,OAAOC,UAAU,EAAC,EAAGF,iBAAAA,EAAmBF,UAAAA,EAAY,CAACK,QAAAA,EAAUC,QAAAA,GAAAA;gBAC7D,IAAIC,KAAAA,CAAMC,OAAO,CAACH,QAAAA,CAAAA,EAAW;AAC3B,oBAAA,OAAOE,MAAME,IAAI,CAAC,IAAIC,GAAAA,CAAIL,QAAAA,CAASM,MAAM,CAACL,QAAAA,CAAAA,CAAAA,CAAAA;AAC5C,gBAAA;gBACA,OAAOM,SAAAA;AACT,YAAA,CAAA,CAAA;AACF,QAAA;QAEA,OAAOV,iBAAAA;AACT,IAAA,CAAA,CAAA;AACF;;;;"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
declare const UPDATED_BY_ATTRIBUTE: string;
|
|
1
2
|
export interface Options {
|
|
2
3
|
user: User;
|
|
3
4
|
isEdition?: boolean;
|
|
@@ -5,6 +6,8 @@ export interface Options {
|
|
|
5
6
|
interface User {
|
|
6
7
|
id: string | number;
|
|
7
8
|
}
|
|
8
|
-
declare const setCreatorFields: <TData extends object>({ user, isEdition }: Options) => <TDataInner extends object = TData>(data: TDataInner) => TDataInner
|
|
9
|
+
declare const setCreatorFields: <TData extends object>({ user, isEdition }: Options) => <TDataInner extends object = TData>(data: TDataInner) => TDataInner & {
|
|
10
|
+
[UPDATED_BY_ATTRIBUTE]: string | number;
|
|
11
|
+
};
|
|
9
12
|
export default setCreatorFields;
|
|
10
13
|
//# sourceMappingURL=set-creator-fields.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-creator-fields.d.ts","sourceRoot":"","sources":["../src/set-creator-fields.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"set-creator-fields.d.ts","sourceRoot":"","sources":["../src/set-creator-fields.ts"],"names":[],"mappings":"AAEA,QAAA,MAA8B,oBAAoB,QAA2B,CAAC;AAE9E,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,UAAU,IAAI;IACZ,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;CACrB;AAED,QAAA,MAAM,gBAAgB,GACnB,KAAK,SAAS,MAAM,EAAE,qBAA6B,OAAO,MAC1D,UAAU,SAAS,MAAM,GAAG,KAAK,EAAE,MAAM,UAAU;IAE9B,CAAC,oBAAoB,CAAC,kBAAS;CAQpD,CAAC;AAEJ,eAAe,gBAAgB,CAAC"}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var fp = require('lodash/fp');
|
|
4
3
|
var contentTypes = require('./content-types.js');
|
|
5
4
|
|
|
6
5
|
const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypes.constants;
|
|
7
6
|
const setCreatorFields = ({ user, isEdition = false })=>(data)=>{
|
|
8
7
|
if (isEdition) {
|
|
9
|
-
return
|
|
8
|
+
return {
|
|
9
|
+
...data,
|
|
10
|
+
[UPDATED_BY_ATTRIBUTE]: user.id
|
|
11
|
+
};
|
|
10
12
|
}
|
|
11
|
-
return
|
|
13
|
+
return {
|
|
14
|
+
...data,
|
|
12
15
|
[CREATED_BY_ATTRIBUTE]: user.id,
|
|
13
16
|
[UPDATED_BY_ATTRIBUTE]: user.id
|
|
14
|
-
}
|
|
17
|
+
};
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
module.exports = setCreatorFields;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-creator-fields.js","sources":["../src/set-creator-fields.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"set-creator-fields.js","sources":["../src/set-creator-fields.ts"],"sourcesContent":["import * as contentTypes from './content-types';\n\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypes.constants;\n\nexport interface Options {\n user: User;\n isEdition?: boolean;\n}\n\ninterface User {\n id: string | number;\n}\n\nconst setCreatorFields =\n <TData extends object>({ user, isEdition = false }: Options) =>\n <TDataInner extends object = TData>(data: TDataInner) => {\n if (isEdition) {\n return { ...data, [UPDATED_BY_ATTRIBUTE]: user.id };\n }\n\n return {\n ...data,\n [CREATED_BY_ATTRIBUTE]: user.id,\n [UPDATED_BY_ATTRIBUTE]: user.id,\n };\n };\n\nexport default setCreatorFields;\n"],"names":["CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypes","setCreatorFields","user","isEdition","data","id"],"mappings":";;;;AAEA,MAAM,EAAEA,oBAAoB,EAAEC,oBAAoB,EAAE,GAAGC,sBAAsB;AAW7E,MAAMC,gBAAAA,GACJ,CAAuB,EAAEC,IAAI,EAAEC,SAAAA,GAAY,KAAK,EAAW,GAC3D,CAAoCC,IAAAA,GAAAA;AAClC,QAAA,IAAID,SAAAA,EAAW;YACb,OAAO;AAAE,gBAAA,GAAGC,IAAI;gBAAE,CAACL,oBAAAA,GAAuBG,IAAAA,CAAKG;AAAG,aAAA;AACpD,QAAA;QAEA,OAAO;AACL,YAAA,GAAGD,IAAI;YACP,CAACN,oBAAAA,GAAuBI,IAAAA,CAAKG,EAAE;YAC/B,CAACN,oBAAAA,GAAuBG,IAAAA,CAAKG;AAC/B,SAAA;AACF,IAAA;;;;"}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
|
-
import { assoc, assign } from 'lodash/fp';
|
|
2
1
|
import { constants } from './content-types.mjs';
|
|
3
2
|
|
|
4
3
|
const { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = constants;
|
|
5
4
|
const setCreatorFields = ({ user, isEdition = false })=>(data)=>{
|
|
6
5
|
if (isEdition) {
|
|
7
|
-
return
|
|
6
|
+
return {
|
|
7
|
+
...data,
|
|
8
|
+
[UPDATED_BY_ATTRIBUTE]: user.id
|
|
9
|
+
};
|
|
8
10
|
}
|
|
9
|
-
return
|
|
11
|
+
return {
|
|
12
|
+
...data,
|
|
10
13
|
[CREATED_BY_ATTRIBUTE]: user.id,
|
|
11
14
|
[UPDATED_BY_ATTRIBUTE]: user.id
|
|
12
|
-
}
|
|
15
|
+
};
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
export { setCreatorFields as default };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"set-creator-fields.mjs","sources":["../src/set-creator-fields.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"set-creator-fields.mjs","sources":["../src/set-creator-fields.ts"],"sourcesContent":["import * as contentTypes from './content-types';\n\nconst { CREATED_BY_ATTRIBUTE, UPDATED_BY_ATTRIBUTE } = contentTypes.constants;\n\nexport interface Options {\n user: User;\n isEdition?: boolean;\n}\n\ninterface User {\n id: string | number;\n}\n\nconst setCreatorFields =\n <TData extends object>({ user, isEdition = false }: Options) =>\n <TDataInner extends object = TData>(data: TDataInner) => {\n if (isEdition) {\n return { ...data, [UPDATED_BY_ATTRIBUTE]: user.id };\n }\n\n return {\n ...data,\n [CREATED_BY_ATTRIBUTE]: user.id,\n [UPDATED_BY_ATTRIBUTE]: user.id,\n };\n };\n\nexport default setCreatorFields;\n"],"names":["CREATED_BY_ATTRIBUTE","UPDATED_BY_ATTRIBUTE","contentTypes","setCreatorFields","user","isEdition","data","id"],"mappings":";;AAEA,MAAM,EAAEA,oBAAoB,EAAEC,oBAAoB,EAAE,GAAGC,SAAsB;AAW7E,MAAMC,gBAAAA,GACJ,CAAuB,EAAEC,IAAI,EAAEC,SAAAA,GAAY,KAAK,EAAW,GAC3D,CAAoCC,IAAAA,GAAAA;AAClC,QAAA,IAAID,SAAAA,EAAW;YACb,OAAO;AAAE,gBAAA,GAAGC,IAAI;gBAAE,CAACL,oBAAAA,GAAuBG,IAAAA,CAAKG;AAAG,aAAA;AACpD,QAAA;QAEA,OAAO;AACL,YAAA,GAAGD,IAAI;YACP,CAACN,oBAAAA,GAAuBI,IAAAA,CAAKG,EAAE;YAC/B,CAACN,oBAAAA,GAAuBG,IAAAA,CAAKG;AAC/B,SAAA;AACF,IAAA;;;;"}
|