@directus/api 20.0.0 → 21.0.0-rc.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.
Files changed (330) hide show
  1. package/dist/app.js +5 -5
  2. package/dist/auth/drivers/ldap.js +5 -5
  3. package/dist/auth/drivers/local.js +4 -4
  4. package/dist/auth/drivers/oauth2.js +5 -5
  5. package/dist/auth/drivers/openid.js +3 -5
  6. package/dist/auth/drivers/saml.js +1 -1
  7. package/dist/auth.js +1 -1
  8. package/dist/cache.js +4 -1
  9. package/dist/cli/commands/bootstrap/index.js +10 -3
  10. package/dist/cli/commands/count/index.js +1 -1
  11. package/dist/cli/commands/database/install.js +1 -1
  12. package/dist/cli/commands/database/migrate.js +1 -1
  13. package/dist/cli/commands/init/index.js +9 -10
  14. package/dist/cli/commands/roles/create.js +1 -1
  15. package/dist/cli/commands/schema/apply.js +1 -1
  16. package/dist/cli/commands/schema/snapshot.js +1 -1
  17. package/dist/cli/commands/users/create.js +1 -1
  18. package/dist/cli/commands/users/passwd.js +1 -1
  19. package/dist/cli/load-extensions.js +1 -1
  20. package/dist/cli/utils/defaults.d.ts +4 -11
  21. package/dist/cli/utils/defaults.js +7 -1
  22. package/dist/constants.d.ts +1 -1
  23. package/dist/constants.js +2 -2
  24. package/dist/controllers/access.d.ts +2 -0
  25. package/dist/controllers/access.js +148 -0
  26. package/dist/controllers/assets.js +1 -1
  27. package/dist/controllers/auth.js +6 -17
  28. package/dist/controllers/files.js +1 -1
  29. package/dist/controllers/permissions.js +14 -2
  30. package/dist/controllers/policies.d.ts +2 -0
  31. package/dist/controllers/policies.js +169 -0
  32. package/dist/controllers/roles.js +22 -1
  33. package/dist/controllers/schema.js +1 -1
  34. package/dist/controllers/tus.js +11 -23
  35. package/dist/controllers/users.js +0 -55
  36. package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +16 -0
  37. package/dist/database/get-ast-from-query/get-ast-from-query.js +82 -0
  38. package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +13 -0
  39. package/dist/database/get-ast-from-query/lib/convert-wildcards.js +69 -0
  40. package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +15 -0
  41. package/dist/database/get-ast-from-query/lib/parse-fields.js +190 -0
  42. package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +14 -0
  43. package/dist/database/get-ast-from-query/utils/get-deep-query.js +17 -0
  44. package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +2 -0
  45. package/dist/database/get-ast-from-query/utils/get-related-collection.js +13 -0
  46. package/dist/database/get-ast-from-query/utils/get-relation.d.ts +2 -0
  47. package/dist/database/get-ast-from-query/utils/get-relation.js +7 -0
  48. package/dist/database/helpers/fn/types.d.ts +2 -1
  49. package/dist/database/helpers/fn/types.js +1 -1
  50. package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
  51. package/dist/database/helpers/geometry/dialects/mssql.js +4 -2
  52. package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
  53. package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
  54. package/dist/database/helpers/geometry/dialects/oracle.js +5 -3
  55. package/dist/database/helpers/geometry/types.d.ts +1 -1
  56. package/dist/database/helpers/geometry/types.js +4 -2
  57. package/dist/database/index.js +3 -2
  58. package/dist/database/migrations/20210518A-add-foreign-key-constraints.js +1 -1
  59. package/dist/database/migrations/20210519A-add-system-fk-triggers.js +1 -1
  60. package/dist/database/migrations/20210802A-replace-groups.js +1 -1
  61. package/dist/database/migrations/20230721A-require-shares-fields.js +1 -1
  62. package/dist/database/migrations/20240710A-permissions-policies.d.ts +3 -0
  63. package/dist/database/migrations/20240710A-permissions-policies.js +169 -0
  64. package/dist/database/migrations/run.js +1 -1
  65. package/dist/database/run-ast/lib/get-db-query.d.ts +4 -0
  66. package/dist/database/run-ast/lib/get-db-query.js +208 -0
  67. package/dist/database/run-ast/lib/parse-current-level.d.ts +7 -0
  68. package/dist/database/run-ast/lib/parse-current-level.js +41 -0
  69. package/dist/database/run-ast/run-ast.d.ts +7 -0
  70. package/dist/database/run-ast/run-ast.js +107 -0
  71. package/dist/database/{run-ast.d.ts → run-ast/types.d.ts} +3 -9
  72. package/dist/database/run-ast/types.js +1 -0
  73. package/dist/database/run-ast/utils/apply-case-when.d.ts +16 -0
  74. package/dist/database/run-ast/utils/apply-case-when.js +26 -0
  75. package/dist/database/run-ast/utils/apply-parent-filters.d.ts +3 -0
  76. package/dist/database/run-ast/utils/apply-parent-filters.js +55 -0
  77. package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +10 -0
  78. package/dist/database/run-ast/utils/get-column-pre-processor.js +57 -0
  79. package/dist/database/run-ast/utils/get-field-alias.d.ts +2 -0
  80. package/dist/database/run-ast/utils/get-field-alias.js +4 -0
  81. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +5 -0
  82. package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +23 -0
  83. package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +3 -0
  84. package/dist/database/run-ast/utils/merge-with-parent-items.js +87 -0
  85. package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +3 -0
  86. package/dist/database/run-ast/utils/remove-temporary-fields.js +73 -0
  87. package/dist/emitter.js +1 -1
  88. package/dist/extensions/lib/get-shared-deps-mapping.js +1 -1
  89. package/dist/extensions/lib/installation/manager.js +1 -1
  90. package/dist/extensions/lib/sandbox/register/call-reference.js +1 -1
  91. package/dist/extensions/lib/sandbox/sdk/generators/log.js +1 -1
  92. package/dist/extensions/lib/sync-extensions.js +1 -1
  93. package/dist/extensions/manager.js +1 -1
  94. package/dist/flows.js +4 -5
  95. package/dist/{logger.js → logger/index.js} +2 -8
  96. package/dist/logger/redact-query.d.ts +1 -0
  97. package/dist/logger/redact-query.js +13 -0
  98. package/dist/mailer.js +1 -1
  99. package/dist/middleware/authenticate.js +2 -7
  100. package/dist/middleware/cache.js +2 -2
  101. package/dist/middleware/error-handler.js +1 -1
  102. package/dist/middleware/rate-limiter-global.js +1 -1
  103. package/dist/middleware/respond.js +2 -2
  104. package/dist/operations/log/index.js +1 -1
  105. package/dist/operations/mail/index.js +1 -1
  106. package/dist/permissions/cache.d.ts +2 -0
  107. package/dist/permissions/cache.js +23 -0
  108. package/dist/permissions/lib/fetch-permissions.d.ts +10 -0
  109. package/dist/permissions/lib/fetch-permissions.js +55 -0
  110. package/dist/permissions/lib/fetch-policies.d.ts +7 -0
  111. package/dist/permissions/lib/fetch-policies.js +28 -0
  112. package/dist/permissions/lib/fetch-roles-tree.d.ts +3 -0
  113. package/dist/permissions/lib/fetch-roles-tree.js +28 -0
  114. package/dist/{services/permissions → permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
  115. package/dist/permissions/lib/with-app-minimal-permissions.js +10 -0
  116. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +7 -0
  117. package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +56 -0
  118. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +3 -0
  119. package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +16 -0
  120. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +8 -0
  121. package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +24 -0
  122. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +9 -0
  123. package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +31 -0
  124. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +16 -0
  125. package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +27 -0
  126. package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +10 -0
  127. package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +23 -0
  128. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +5 -0
  129. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +7 -0
  130. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +5 -0
  131. package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +10 -0
  132. package/dist/permissions/modules/fetch-global-access/types.d.ts +4 -0
  133. package/dist/permissions/modules/fetch-global-access/types.js +1 -0
  134. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +4 -0
  135. package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +27 -0
  136. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +12 -0
  137. package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +32 -0
  138. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +4 -0
  139. package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +29 -0
  140. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +4 -0
  141. package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +49 -0
  142. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +3 -0
  143. package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +56 -0
  144. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +4 -0
  145. package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +8 -0
  146. package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +9 -0
  147. package/dist/permissions/modules/process-ast/lib/inject-cases.js +93 -0
  148. package/dist/permissions/modules/process-ast/process-ast.d.ts +9 -0
  149. package/dist/permissions/modules/process-ast/process-ast.js +39 -0
  150. package/dist/permissions/modules/process-ast/types.d.ts +24 -0
  151. package/dist/permissions/modules/process-ast/types.js +1 -0
  152. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +2 -0
  153. package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +7 -0
  154. package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +12 -0
  155. package/dist/permissions/modules/process-ast/utils/dedupe-access.js +30 -0
  156. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +15 -0
  157. package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +50 -0
  158. package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +3 -0
  159. package/dist/permissions/modules/process-ast/utils/find-related-collection.js +9 -0
  160. package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +3 -0
  161. package/dist/permissions/modules/process-ast/utils/flatten-filter.js +34 -0
  162. package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +1 -0
  163. package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +3 -0
  164. package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +5 -0
  165. package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +7 -0
  166. package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +2 -0
  167. package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +3 -0
  168. package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +2 -0
  169. package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +3 -0
  170. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +3 -0
  171. package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +16 -0
  172. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +2 -0
  173. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +12 -0
  174. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +2 -0
  175. package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +28 -0
  176. package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +5 -0
  177. package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +12 -0
  178. package/dist/permissions/modules/process-payload/process-payload.d.ts +13 -0
  179. package/dist/permissions/modules/process-payload/process-payload.js +77 -0
  180. package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +12 -0
  181. package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +11 -0
  182. package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +9 -0
  183. package/dist/permissions/modules/validate-access/lib/validate-item-access.js +33 -0
  184. package/dist/permissions/modules/validate-access/validate-access.d.ts +14 -0
  185. package/dist/permissions/modules/validate-access/validate-access.js +28 -0
  186. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +1 -0
  187. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +8 -0
  188. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +5 -0
  189. package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +10 -0
  190. package/dist/permissions/types.d.ts +6 -0
  191. package/dist/permissions/types.js +1 -0
  192. package/dist/permissions/utils/create-default-accountability.d.ts +2 -0
  193. package/dist/permissions/utils/create-default-accountability.js +11 -0
  194. package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +8 -0
  195. package/dist/permissions/utils/extract-required-dynamic-variable-context.js +27 -0
  196. package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +9 -0
  197. package/dist/permissions/utils/fetch-dynamic-variable-context.js +43 -0
  198. package/dist/permissions/utils/filter-policies-by-ip.d.ts +2 -0
  199. package/dist/permissions/utils/filter-policies-by-ip.js +15 -0
  200. package/dist/permissions/utils/get-unaliased-field-key.d.ts +5 -0
  201. package/dist/permissions/utils/get-unaliased-field-key.js +17 -0
  202. package/dist/permissions/utils/process-permissions.d.ts +7 -0
  203. package/dist/permissions/utils/process-permissions.js +9 -0
  204. package/dist/permissions/utils/with-cache.d.ts +10 -0
  205. package/dist/permissions/utils/with-cache.js +25 -0
  206. package/dist/request/is-denied-ip.js +1 -1
  207. package/dist/server.js +1 -1
  208. package/dist/services/access.d.ts +10 -0
  209. package/dist/services/access.js +43 -0
  210. package/dist/services/activity.js +23 -11
  211. package/dist/services/assets.d.ts +2 -3
  212. package/dist/services/assets.js +11 -6
  213. package/dist/services/authentication.js +18 -18
  214. package/dist/services/collections.js +18 -17
  215. package/dist/services/fields.d.ts +0 -1
  216. package/dist/services/fields.js +53 -24
  217. package/dist/services/files/utils/get-metadata.js +1 -1
  218. package/dist/services/files.js +25 -15
  219. package/dist/services/graphql/index.d.ts +3 -3
  220. package/dist/services/graphql/index.js +126 -22
  221. package/dist/services/graphql/subscription.js +2 -4
  222. package/dist/services/graphql/utils/process-error.js +1 -1
  223. package/dist/services/graphql/utils/sanitize-gql-schema.js +1 -1
  224. package/dist/services/import-export.js +19 -5
  225. package/dist/services/index.d.ts +3 -2
  226. package/dist/services/index.js +3 -2
  227. package/dist/services/items.d.ts +3 -3
  228. package/dist/services/items.js +115 -44
  229. package/dist/services/mail/index.js +1 -1
  230. package/dist/services/meta.js +60 -23
  231. package/dist/services/notifications.js +15 -7
  232. package/dist/services/payload.d.ts +9 -10
  233. package/dist/services/payload.js +18 -3
  234. package/dist/services/{permissions/index.d.ts → permissions.d.ts} +5 -7
  235. package/dist/services/{permissions/index.js → permissions.js} +30 -54
  236. package/dist/services/policies.d.ts +12 -0
  237. package/dist/services/policies.js +87 -0
  238. package/dist/services/relations.d.ts +0 -6
  239. package/dist/services/relations.js +26 -29
  240. package/dist/services/roles.d.ts +4 -12
  241. package/dist/services/roles.js +57 -424
  242. package/dist/services/server.js +1 -1
  243. package/dist/services/shares.d.ts +0 -2
  244. package/dist/services/shares.js +13 -9
  245. package/dist/services/specifications.d.ts +2 -2
  246. package/dist/services/specifications.js +39 -27
  247. package/dist/services/tus/data-store.js +3 -1
  248. package/dist/services/users.d.ts +1 -5
  249. package/dist/services/users.js +79 -162
  250. package/dist/services/utils.js +11 -7
  251. package/dist/services/versions.d.ts +0 -2
  252. package/dist/services/versions.js +34 -10
  253. package/dist/services/webhooks.js +1 -1
  254. package/dist/telemetry/lib/get-report.js +2 -2
  255. package/dist/telemetry/lib/track.js +1 -1
  256. package/dist/telemetry/utils/check-user-limits.d.ts +5 -0
  257. package/dist/telemetry/utils/check-user-limits.js +19 -0
  258. package/dist/types/ast.d.ts +43 -1
  259. package/dist/types/items.d.ts +11 -0
  260. package/dist/utils/apply-diff.js +1 -1
  261. package/dist/utils/apply-query.d.ts +4 -3
  262. package/dist/utils/apply-query.js +37 -8
  263. package/dist/utils/delete-from-require-cache.js +1 -1
  264. package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +17 -0
  265. package/dist/utils/fetch-user-count/fetch-access-lookup.js +22 -0
  266. package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +16 -0
  267. package/dist/utils/fetch-user-count/fetch-access-roles.js +37 -0
  268. package/dist/utils/fetch-user-count/fetch-active-users.d.ts +6 -0
  269. package/dist/utils/fetch-user-count/fetch-active-users.js +3 -0
  270. package/dist/utils/fetch-user-count/fetch-user-count.d.ts +12 -0
  271. package/dist/utils/fetch-user-count/fetch-user-count.js +57 -0
  272. package/dist/utils/fetch-user-count/get-user-count-query.d.ts +20 -0
  273. package/dist/utils/fetch-user-count/get-user-count-query.js +17 -0
  274. package/dist/utils/get-accountability-for-role.js +16 -25
  275. package/dist/utils/get-accountability-for-token.js +17 -16
  276. package/dist/utils/get-cache-key.d.ts +1 -1
  277. package/dist/utils/get-cache-key.js +12 -1
  278. package/dist/utils/get-column.d.ts +2 -1
  279. package/dist/utils/get-column.js +1 -0
  280. package/dist/utils/get-default-value.js +1 -1
  281. package/dist/utils/get-ip-from-req.js +1 -1
  282. package/dist/utils/get-schema.js +1 -1
  283. package/dist/utils/get-service.js +5 -1
  284. package/dist/utils/is-url-allowed.js +1 -1
  285. package/dist/utils/reduce-schema.d.ts +4 -6
  286. package/dist/utils/reduce-schema.js +16 -32
  287. package/dist/utils/sanitize-query.js +1 -1
  288. package/dist/utils/transaction.js +1 -1
  289. package/dist/utils/validate-env.js +1 -1
  290. package/dist/utils/validate-storage.js +1 -1
  291. package/dist/utils/validate-user-count-integrity.d.ts +13 -0
  292. package/dist/utils/validate-user-count-integrity.js +29 -0
  293. package/dist/websocket/authenticate.d.ts +0 -2
  294. package/dist/websocket/authenticate.js +0 -12
  295. package/dist/websocket/controllers/base.d.ts +1 -1
  296. package/dist/websocket/controllers/base.js +21 -17
  297. package/dist/websocket/controllers/graphql.js +2 -5
  298. package/dist/websocket/controllers/hooks.js +4 -0
  299. package/dist/websocket/controllers/rest.js +1 -3
  300. package/dist/websocket/errors.js +1 -1
  301. package/dist/websocket/handlers/subscribe.js +0 -2
  302. package/dist/websocket/utils/items.d.ts +1 -1
  303. package/package.json +28 -27
  304. package/dist/database/run-ast.js +0 -450
  305. package/dist/middleware/check-ip.d.ts +0 -2
  306. package/dist/middleware/check-ip.js +0 -37
  307. package/dist/middleware/get-permissions.d.ts +0 -3
  308. package/dist/middleware/get-permissions.js +0 -10
  309. package/dist/services/authorization.d.ts +0 -17
  310. package/dist/services/authorization.js +0 -456
  311. package/dist/services/permissions/lib/with-app-minimal-permissions.js +0 -13
  312. package/dist/telemetry/utils/check-increased-user-limits.d.ts +0 -7
  313. package/dist/telemetry/utils/check-increased-user-limits.js +0 -25
  314. package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +0 -6
  315. package/dist/telemetry/utils/get-role-counts-by-roles.js +0 -27
  316. package/dist/telemetry/utils/get-role-counts-by-users.d.ts +0 -11
  317. package/dist/telemetry/utils/get-role-counts-by-users.js +0 -34
  318. package/dist/telemetry/utils/get-user-count.d.ts +0 -8
  319. package/dist/telemetry/utils/get-user-count.js +0 -33
  320. package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +0 -7
  321. package/dist/telemetry/utils/get-user-counts-by-roles.js +0 -35
  322. package/dist/utils/get-ast-from-query.d.ts +0 -13
  323. package/dist/utils/get-ast-from-query.js +0 -297
  324. package/dist/utils/get-permissions.d.ts +0 -2
  325. package/dist/utils/get-permissions.js +0 -150
  326. package/dist/utils/merge-permissions-for-share.d.ts +0 -4
  327. package/dist/utils/merge-permissions-for-share.js +0 -109
  328. package/dist/utils/merge-permissions.d.ts +0 -3
  329. package/dist/utils/merge-permissions.js +0 -95
  330. /package/dist/{logger.d.ts → logger/index.d.ts} +0 -0
@@ -3,8 +3,9 @@ import { ErrorCode, InvalidPayloadError, isDirectusError } from '@directus/error
3
3
  import { Router } from 'express';
4
4
  import { createLDAPAuthRouter, createLocalAuthRouter, createOAuth2AuthRouter, createOpenIDAuthRouter, createSAMLAuthRouter, } from '../auth/drivers/index.js';
5
5
  import { DEFAULT_AUTH_PROVIDER, REFRESH_COOKIE_OPTIONS, SESSION_COOKIE_OPTIONS } from '../constants.js';
6
- import { useLogger } from '../logger.js';
6
+ import { useLogger } from '../logger/index.js';
7
7
  import { respond } from '../middleware/respond.js';
8
+ import { createDefaultAccountability } from '../permissions/utils/create-default-accountability.js';
8
9
  import { AuthenticationService } from '../services/authentication.js';
9
10
  import { UsersService } from '../services/users.js';
10
11
  import asyncHandler from '../utils/async-handler.js';
@@ -71,10 +72,7 @@ function getCurrentRefreshToken(req, mode) {
71
72
  return undefined;
72
73
  }
73
74
  router.post('/refresh', asyncHandler(async (req, res, next) => {
74
- const accountability = {
75
- ip: getIPFromReq(req),
76
- role: null,
77
- };
75
+ const accountability = createDefaultAccountability({ ip: getIPFromReq(req) });
78
76
  const userAgent = req.get('user-agent')?.substring(0, 1024);
79
77
  if (userAgent)
80
78
  accountability.userAgent = userAgent;
@@ -111,10 +109,7 @@ router.post('/refresh', asyncHandler(async (req, res, next) => {
111
109
  return next();
112
110
  }), respond);
113
111
  router.post('/logout', asyncHandler(async (req, res, next) => {
114
- const accountability = {
115
- ip: getIPFromReq(req),
116
- role: null,
117
- };
112
+ const accountability = createDefaultAccountability({ ip: getIPFromReq(req) });
118
113
  const userAgent = req.get('user-agent')?.substring(0, 1024);
119
114
  if (userAgent)
120
115
  accountability.userAgent = userAgent;
@@ -145,10 +140,7 @@ router.post('/password/request', asyncHandler(async (req, _res, next) => {
145
140
  if (typeof req.body.email !== 'string') {
146
141
  throw new InvalidPayloadError({ reason: `"email" field is required` });
147
142
  }
148
- const accountability = {
149
- ip: getIPFromReq(req),
150
- role: null,
151
- };
143
+ const accountability = createDefaultAccountability({ ip: getIPFromReq(req) });
152
144
  const userAgent = req.get('user-agent')?.substring(0, 1024);
153
145
  if (userAgent)
154
146
  accountability.userAgent = userAgent;
@@ -177,10 +169,7 @@ router.post('/password/reset', asyncHandler(async (req, _res, next) => {
177
169
  if (typeof req.body.password !== 'string') {
178
170
  throw new InvalidPayloadError({ reason: `"password" field is required` });
179
171
  }
180
- const accountability = {
181
- ip: getIPFromReq(req),
182
- role: null,
183
- };
172
+ const accountability = createDefaultAccountability({ ip: getIPFromReq(req) });
184
173
  const userAgent = req.get('user-agent')?.substring(0, 1024);
185
174
  if (userAgent)
186
175
  accountability.userAgent = userAgent;
@@ -35,7 +35,7 @@ export const multipartHandler = (req, res, next) => {
35
35
  headers,
36
36
  defParamCharset: 'utf8',
37
37
  limits: {
38
- fileSize: env['FILES_MAX_UPLOAD_SIZE'] ? bytes(env['FILES_MAX_UPLOAD_SIZE']) : undefined,
38
+ fileSize: env['FILES_MAX_UPLOAD_SIZE'] ? bytes.parse(env['FILES_MAX_UPLOAD_SIZE']) : undefined,
39
39
  },
40
40
  });
41
41
  const savedFiles = [];
@@ -1,10 +1,12 @@
1
- import { ErrorCode, isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, ForbiddenError, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
+ import getDatabase from '../database/index.js';
3
4
  import { respond } from '../middleware/respond.js';
4
5
  import useCollection from '../middleware/use-collection.js';
5
6
  import { validateBatch } from '../middleware/validate-batch.js';
7
+ import { fetchAccountabilityCollectionAccess } from '../permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js';
6
8
  import { MetaService } from '../services/meta.js';
7
- import { PermissionsService } from '../services/permissions/index.js';
9
+ import { PermissionsService } from '../services/permissions.js';
8
10
  import asyncHandler from '../utils/async-handler.js';
9
11
  import { sanitizeQuery } from '../utils/sanitize-query.js';
10
12
  const router = express.Router();
@@ -69,6 +71,16 @@ const readHandler = asyncHandler(async (req, res, next) => {
69
71
  });
70
72
  router.get('/', validateBatch('read'), readHandler, respond);
71
73
  router.search('/', validateBatch('read'), readHandler, respond);
74
+ router.get('/me', asyncHandler(async (req, res, next) => {
75
+ if (!req.accountability?.user && !req.accountability?.role)
76
+ throw new ForbiddenError();
77
+ const result = await fetchAccountabilityCollectionAccess(req.accountability, {
78
+ schema: req.schema,
79
+ knex: getDatabase(),
80
+ });
81
+ res.locals['payload'] = { data: result };
82
+ return next();
83
+ }), respond);
72
84
  router.get('/:pk', asyncHandler(async (req, res, next) => {
73
85
  if (req.path.endsWith('me'))
74
86
  return next();
@@ -0,0 +1,2 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
@@ -0,0 +1,169 @@
1
+ import { ErrorCode, ForbiddenError, isDirectusError } from '@directus/errors';
2
+ import express from 'express';
3
+ import getDatabase from '../database/index.js';
4
+ import { respond } from '../middleware/respond.js';
5
+ import useCollection from '../middleware/use-collection.js';
6
+ import { validateBatch } from '../middleware/validate-batch.js';
7
+ import { fetchAccountabilityPolicyGlobals } from '../permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js';
8
+ import { MetaService } from '../services/meta.js';
9
+ import { PoliciesService } from '../services/policies.js';
10
+ import asyncHandler from '../utils/async-handler.js';
11
+ import { sanitizeQuery } from '../utils/sanitize-query.js';
12
+ const router = express.Router();
13
+ router.use(useCollection('directus_policies'));
14
+ router.post('/', asyncHandler(async (req, res, next) => {
15
+ const service = new PoliciesService({
16
+ accountability: req.accountability,
17
+ schema: req.schema,
18
+ });
19
+ const savedKeys = [];
20
+ if (Array.isArray(req.body)) {
21
+ const keys = await service.createMany(req.body);
22
+ savedKeys.push(...keys);
23
+ }
24
+ else {
25
+ const key = await service.createOne(req.body);
26
+ savedKeys.push(key);
27
+ }
28
+ try {
29
+ if (Array.isArray(req.body)) {
30
+ const items = await service.readMany(savedKeys, req.sanitizedQuery);
31
+ res.locals['payload'] = { data: items };
32
+ }
33
+ else {
34
+ const item = await service.readOne(savedKeys[0], req.sanitizedQuery);
35
+ res.locals['payload'] = { data: item };
36
+ }
37
+ }
38
+ catch (error) {
39
+ if (isDirectusError(error, ErrorCode.Forbidden)) {
40
+ return next();
41
+ }
42
+ throw error;
43
+ }
44
+ return next();
45
+ }), respond);
46
+ const readHandler = asyncHandler(async (req, res, next) => {
47
+ const service = new PoliciesService({
48
+ accountability: req.accountability,
49
+ schema: req.schema,
50
+ });
51
+ const metaService = new MetaService({
52
+ accountability: req.accountability,
53
+ schema: req.schema,
54
+ });
55
+ let result;
56
+ if (req.body.keys) {
57
+ result = await service.readMany(req.body.keys, req.sanitizedQuery);
58
+ }
59
+ else {
60
+ result = await service.readByQuery(req.sanitizedQuery);
61
+ }
62
+ const meta = await metaService.getMetaForQuery('directus_policies', req.sanitizedQuery);
63
+ res.locals['payload'] = { data: result, meta };
64
+ return next();
65
+ });
66
+ router.get('/', validateBatch('read'), readHandler, respond);
67
+ router.search('/', validateBatch('read'), readHandler, respond);
68
+ router.get('/me/globals', asyncHandler(async (req, res, next) => {
69
+ try {
70
+ if (!req.accountability?.user && !req.accountability?.role)
71
+ throw new ForbiddenError();
72
+ const result = await fetchAccountabilityPolicyGlobals(req.accountability, {
73
+ schema: req.schema,
74
+ knex: getDatabase(),
75
+ });
76
+ res.locals['payload'] = { data: result };
77
+ }
78
+ catch (error) {
79
+ if (isDirectusError(error, ErrorCode.Forbidden)) {
80
+ res.locals['payload'] = { data: { app_access: false } };
81
+ return next();
82
+ }
83
+ throw error;
84
+ }
85
+ return next();
86
+ }), respond);
87
+ router.get('/:pk', asyncHandler(async (req, res, next) => {
88
+ if (req.path.endsWith('me'))
89
+ return next();
90
+ const service = new PoliciesService({
91
+ accountability: req.accountability,
92
+ schema: req.schema,
93
+ });
94
+ const record = await service.readOne(req.params['pk'], req.sanitizedQuery);
95
+ res.locals['payload'] = { data: record };
96
+ return next();
97
+ }), respond);
98
+ router.patch('/', validateBatch('update'), asyncHandler(async (req, res, next) => {
99
+ const service = new PoliciesService({
100
+ accountability: req.accountability,
101
+ schema: req.schema,
102
+ });
103
+ let keys = [];
104
+ if (Array.isArray(req.body)) {
105
+ keys = await service.updateBatch(req.body);
106
+ }
107
+ else if (req.body.keys) {
108
+ keys = await service.updateMany(req.body.keys, req.body.data);
109
+ }
110
+ else {
111
+ const sanitizedQuery = sanitizeQuery(req.body.query, req.accountability);
112
+ keys = await service.updateByQuery(sanitizedQuery, req.body.data);
113
+ }
114
+ try {
115
+ const result = await service.readMany(keys, req.sanitizedQuery);
116
+ res.locals['payload'] = { data: result };
117
+ }
118
+ catch (error) {
119
+ if (isDirectusError(error, ErrorCode.Forbidden)) {
120
+ return next();
121
+ }
122
+ throw error;
123
+ }
124
+ return next();
125
+ }), respond);
126
+ router.patch('/:pk', asyncHandler(async (req, res, next) => {
127
+ const service = new PoliciesService({
128
+ accountability: req.accountability,
129
+ schema: req.schema,
130
+ });
131
+ const primaryKey = await service.updateOne(req.params['pk'], req.body);
132
+ try {
133
+ const item = await service.readOne(primaryKey, req.sanitizedQuery);
134
+ res.locals['payload'] = { data: item || null };
135
+ }
136
+ catch (error) {
137
+ if (isDirectusError(error, ErrorCode.Forbidden)) {
138
+ return next();
139
+ }
140
+ throw error;
141
+ }
142
+ return next();
143
+ }), respond);
144
+ router.delete('/', validateBatch('delete'), asyncHandler(async (req, _res, next) => {
145
+ const service = new PoliciesService({
146
+ accountability: req.accountability,
147
+ schema: req.schema,
148
+ });
149
+ if (Array.isArray(req.body)) {
150
+ await service.deleteMany(req.body);
151
+ }
152
+ else if (req.body.keys) {
153
+ await service.deleteMany(req.body.keys);
154
+ }
155
+ else {
156
+ const sanitizedQuery = sanitizeQuery(req.body.query, req.accountability);
157
+ await service.deleteByQuery(sanitizedQuery);
158
+ }
159
+ return next();
160
+ }), respond);
161
+ router.delete('/:pk', asyncHandler(async (req, _res, next) => {
162
+ const service = new PoliciesService({
163
+ accountability: req.accountability,
164
+ schema: req.schema,
165
+ });
166
+ await service.deleteOne(req.params['pk']);
167
+ return next();
168
+ }), respond);
169
+ export default router;
@@ -1,4 +1,4 @@
1
- import { ErrorCode, isDirectusError } from '@directus/errors';
1
+ import { ErrorCode, ForbiddenError, isDirectusError } from '@directus/errors';
2
2
  import express from 'express';
3
3
  import { respond } from '../middleware/respond.js';
4
4
  import useCollection from '../middleware/use-collection.js';
@@ -57,6 +57,27 @@ const readHandler = asyncHandler(async (req, res, next) => {
57
57
  });
58
58
  router.get('/', validateBatch('read'), readHandler, respond);
59
59
  router.search('/', validateBatch('read'), readHandler, respond);
60
+ router.get('/me', asyncHandler(async (req, res, next) => {
61
+ if (!req.accountability?.user && !req.accountability?.role)
62
+ throw new ForbiddenError();
63
+ const service = new RolesService({
64
+ accountability: req.accountability,
65
+ schema: req.schema,
66
+ });
67
+ const query = { ...req.sanitizedQuery, limit: -1 };
68
+ try {
69
+ const roles = await service.readMany(req.accountability.roles, query);
70
+ res.locals['payload'] = { data: roles || null };
71
+ }
72
+ catch (error) {
73
+ if (isDirectusError(error, ErrorCode.Forbidden)) {
74
+ res.locals['payload'] = { data: req.accountability.roles.map((id) => ({ id })) };
75
+ return next();
76
+ }
77
+ throw error;
78
+ }
79
+ return next();
80
+ }), respond);
60
81
  router.get('/:pk', asyncHandler(async (req, res, next) => {
61
82
  const service = new RolesService({
62
83
  accountability: req.accountability,
@@ -3,7 +3,7 @@ import { parseJSON } from '@directus/utils';
3
3
  import Busboy from 'busboy';
4
4
  import express from 'express';
5
5
  import { load as loadYaml } from 'js-yaml';
6
- import { useLogger } from '../logger.js';
6
+ import { useLogger } from '../logger/index.js';
7
7
  import { respond } from '../middleware/respond.js';
8
8
  import { SchemaService } from '../services/schema.js';
9
9
  import asyncHandler from '../utils/async-handler.js';
@@ -1,10 +1,10 @@
1
1
  import { Router } from 'express';
2
+ import getDatabase from '../database/index.js';
3
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
2
4
  import { getSchema } from '../utils/get-schema.js';
3
5
  import { scheduleSynchronizedJob, validateCron } from '../utils/schedule.js';
4
6
  import { createTusServer } from '../services/tus/index.js';
5
- import { AuthorizationService } from '../services/authorization.js';
6
7
  import asyncHandler from '../utils/async-handler.js';
7
- import { ForbiddenError } from '@directus/errors';
8
8
  import { RESUMABLE_UPLOADS } from '../constants.js';
9
9
  const mapAction = (method) => {
10
10
  switch (method) {
@@ -19,28 +19,16 @@ const mapAction = (method) => {
19
19
  }
20
20
  };
21
21
  const checkFileAccess = asyncHandler(async (req, _res, next) => {
22
- const auth = new AuthorizationService({
23
- accountability: req.accountability,
24
- schema: req.schema,
25
- });
26
- if (!req.accountability?.admin) {
22
+ if (req.accountability) {
27
23
  const action = mapAction(req.method);
28
- if (action === 'create') {
29
- // checkAccess doesnt seem to work as expected for "create" actions
30
- const hasPermission = Boolean(req.accountability?.permissions?.find((permission) => {
31
- return permission.collection === 'directus_files' && permission.action === action;
32
- }));
33
- if (!hasPermission)
34
- throw new ForbiddenError();
35
- }
36
- else {
37
- try {
38
- await auth.checkAccess(action, 'directus_files');
39
- }
40
- catch (e) {
41
- throw new ForbiddenError();
42
- }
43
- }
24
+ await validateAccess({
25
+ action,
26
+ collection: 'directus_files',
27
+ accountability: req.accountability,
28
+ }, {
29
+ schema: req.schema,
30
+ knex: getDatabase(),
31
+ });
44
32
  }
45
33
  return next();
46
34
  });
@@ -7,7 +7,6 @@ import useCollection from '../middleware/use-collection.js';
7
7
  import { validateBatch } from '../middleware/validate-batch.js';
8
8
  import { AuthenticationService } from '../services/authentication.js';
9
9
  import { MetaService } from '../services/meta.js';
10
- import { RolesService } from '../services/roles.js';
11
10
  import { TFAService } from '../services/tfa.js';
12
11
  import { UsersService } from '../services/users.js';
13
12
  import asyncHandler from '../utils/async-handler.js';
@@ -262,33 +261,6 @@ router.post('/me/tfa/enable/', asyncHandler(async (req, _res, next) => {
262
261
  if (!req.body.otp) {
263
262
  throw new InvalidPayloadError({ reason: `"otp" is required` });
264
263
  }
265
- // Override permissions only when enforce TFA is enabled in role
266
- if (req.accountability.role) {
267
- const rolesService = new RolesService({
268
- schema: req.schema,
269
- });
270
- const role = (await rolesService.readOne(req.accountability.role));
271
- if (role && role.enforce_tfa) {
272
- const existingPermission = await req.accountability.permissions?.find((p) => p.collection === 'directus_users' && p.action === 'update');
273
- if (existingPermission) {
274
- existingPermission.fields = ['tfa_secret'];
275
- existingPermission.permissions = { id: { _eq: req.accountability.user } };
276
- existingPermission.presets = null;
277
- existingPermission.validation = null;
278
- }
279
- else {
280
- (req.accountability.permissions || (req.accountability.permissions = [])).push({
281
- action: 'update',
282
- collection: 'directus_users',
283
- fields: ['tfa_secret'],
284
- permissions: { id: { _eq: req.accountability.user } },
285
- presets: null,
286
- role: req.accountability.role,
287
- validation: null,
288
- });
289
- }
290
- }
291
- }
292
264
  const service = new TFAService({
293
265
  accountability: req.accountability,
294
266
  schema: req.schema,
@@ -303,33 +275,6 @@ router.post('/me/tfa/disable', asyncHandler(async (req, _res, next) => {
303
275
  if (!req.body.otp) {
304
276
  throw new InvalidPayloadError({ reason: `"otp" is required` });
305
277
  }
306
- // Override permissions only when enforce TFA is enabled in role
307
- if (req.accountability.role) {
308
- const rolesService = new RolesService({
309
- schema: req.schema,
310
- });
311
- const role = (await rolesService.readOne(req.accountability.role));
312
- if (role && role.enforce_tfa) {
313
- const existingPermission = await req.accountability.permissions?.find((p) => p.collection === 'directus_users' && p.action === 'update');
314
- if (existingPermission) {
315
- existingPermission.fields = ['tfa_secret'];
316
- existingPermission.permissions = { id: { _eq: req.accountability.user } };
317
- existingPermission.presets = null;
318
- existingPermission.validation = null;
319
- }
320
- else {
321
- (req.accountability.permissions || (req.accountability.permissions = [])).push({
322
- action: 'update',
323
- collection: 'directus_users',
324
- fields: ['tfa_secret'],
325
- permissions: { id: { _eq: req.accountability.user } },
326
- presets: null,
327
- role: req.accountability.role,
328
- validation: null,
329
- });
330
- }
331
- }
332
- }
333
278
  const service = new TFAService({
334
279
  accountability: req.accountability,
335
280
  schema: req.schema,
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Generate an AST based on a given collection and query
3
+ */
4
+ import type { Accountability, Query, SchemaOverview } from '@directus/types';
5
+ import type { Knex } from 'knex';
6
+ import type { AST } from '../../types/index.js';
7
+ export interface GetAstFromQueryOptions {
8
+ collection: string;
9
+ query: Query;
10
+ accountability: Accountability | null;
11
+ }
12
+ export interface GetAstFromQueryContext {
13
+ knex: Knex;
14
+ schema: SchemaOverview;
15
+ }
16
+ export declare function getAstFromQuery(options: GetAstFromQueryOptions, context: GetAstFromQueryContext): Promise<AST>;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Generate an AST based on a given collection and query
3
+ */
4
+ import { cloneDeep, uniq } from 'lodash-es';
5
+ import { fetchAllowedFields } from '../../permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js';
6
+ import { parseFields } from './lib/parse-fields.js';
7
+ export async function getAstFromQuery(options, context) {
8
+ options.query = cloneDeep(options.query);
9
+ const ast = {
10
+ type: 'root',
11
+ name: options.collection,
12
+ query: options.query,
13
+ children: [],
14
+ cases: [],
15
+ };
16
+ let fields = ['*'];
17
+ if (options.query.fields) {
18
+ fields = options.query.fields;
19
+ }
20
+ /**
21
+ * When using aggregate functions, you can't have any other regular fields
22
+ * selected. This makes sure you never end up in a non-aggregate fields selection error
23
+ */
24
+ if (Object.keys(options.query.aggregate || {}).length > 0) {
25
+ fields = [];
26
+ }
27
+ /**
28
+ * Similarly, when grouping on a specific field, you can't have other non-aggregated fields.
29
+ * The group query will override the fields query
30
+ */
31
+ if (options.query.group) {
32
+ fields = options.query.group;
33
+ }
34
+ fields = uniq(fields);
35
+ const deep = options.query.deep || {};
36
+ // Prevent fields/deep from showing up in the query object in further use
37
+ delete options.query.fields;
38
+ delete options.query.deep;
39
+ if (!options.query.sort) {
40
+ // We'll default to the primary key for the standard sort output
41
+ let sortField = context.schema.collections[options.collection].primary;
42
+ // If a custom manual sort field is configured, use that
43
+ if (context.schema.collections[options.collection]?.sortField) {
44
+ sortField = context.schema.collections[options.collection].sortField;
45
+ }
46
+ if (options.accountability && options.accountability.admin === false) {
47
+ // Verify that the user has access to the sort field
48
+ const allowedFields = await fetchAllowedFields({
49
+ collection: options.collection,
50
+ action: 'read',
51
+ accountability: options.accountability,
52
+ }, context);
53
+ if (allowedFields.length === 0) {
54
+ sortField = null;
55
+ }
56
+ else if (allowedFields.includes('*') === false && allowedFields.includes(sortField) === false) {
57
+ // If the sort field is not allowed, default to the first allowed field
58
+ sortField = allowedFields[0];
59
+ }
60
+ }
61
+ // When group by is used, default to the first column provided in the group by clause
62
+ if (options.query.group?.[0]) {
63
+ sortField = options.query.group[0];
64
+ }
65
+ if (sortField) {
66
+ options.query.sort = [sortField];
67
+ }
68
+ }
69
+ // When no group by is supplied, but an aggregate function is used, only a single row will be
70
+ // returned. In those cases, we'll ignore the sort field altogether
71
+ if (options.query.aggregate && Object.keys(options.query.aggregate).length && !options.query.group?.[0]) {
72
+ delete options.query.sort;
73
+ }
74
+ ast.children = await parseFields({
75
+ parentCollection: options.collection,
76
+ fields,
77
+ query: options.query,
78
+ deep,
79
+ accountability: options.accountability,
80
+ }, context);
81
+ return ast;
82
+ }
@@ -0,0 +1,13 @@
1
+ import type { Accountability, Query, SchemaOverview } from '@directus/types';
2
+ import type { Knex } from 'knex';
3
+ export interface ConvertWildcardsOptions {
4
+ parentCollection: string;
5
+ fields: string[];
6
+ query: Query;
7
+ accountability: Accountability | null;
8
+ }
9
+ export interface ConvertWildCardsContext {
10
+ schema: SchemaOverview;
11
+ knex: Knex;
12
+ }
13
+ export declare function convertWildcards(options: ConvertWildcardsOptions, context: ConvertWildCardsContext): Promise<string[]>;
@@ -0,0 +1,69 @@
1
+ import { cloneDeep } from 'lodash-es';
2
+ import { fetchAllowedFields } from '../../../permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js';
3
+ import { getRelation } from '../utils/get-relation.js';
4
+ export async function convertWildcards(options, context) {
5
+ const fields = cloneDeep(options.fields);
6
+ const fieldsInCollection = Object.entries(context.schema.collections[options.parentCollection].fields).map(([name]) => name);
7
+ let allowedFields = fieldsInCollection;
8
+ if (options.accountability && options.accountability.admin === false) {
9
+ allowedFields = await fetchAllowedFields({
10
+ collection: options.parentCollection,
11
+ action: 'read',
12
+ accountability: options.accountability,
13
+ }, context);
14
+ }
15
+ if (!allowedFields || allowedFields.length === 0)
16
+ return [];
17
+ // In case of full read permissions
18
+ if (allowedFields[0] === '*')
19
+ allowedFields = fieldsInCollection;
20
+ for (let index = 0; index < fields.length; index++) {
21
+ const fieldKey = fields[index];
22
+ if (fieldKey.includes('*') === false)
23
+ continue;
24
+ if (fieldKey === '*') {
25
+ const aliases = Object.keys(options.query.alias ?? {});
26
+ // Set to all fields in collection
27
+ if (allowedFields.includes('*')) {
28
+ fields.splice(index, 1, ...fieldsInCollection, ...aliases);
29
+ }
30
+ else {
31
+ // Set to all allowed fields
32
+ const allowedAliases = aliases.filter((fieldKey) => {
33
+ const name = options.query.alias[fieldKey];
34
+ return allowedFields.includes(name);
35
+ });
36
+ fields.splice(index, 1, ...allowedFields, ...allowedAliases);
37
+ }
38
+ }
39
+ // Swap *.* case for *,<relational-field>.*,<another-relational>.*
40
+ if (fieldKey.includes('.') && fieldKey.split('.')[0] === '*') {
41
+ const parts = fieldKey.split('.');
42
+ const relationalFields = allowedFields.includes('*')
43
+ ? context.schema.relations
44
+ .filter((relation) => relation.collection === options.parentCollection ||
45
+ relation.related_collection === options.parentCollection)
46
+ .map((relation) => {
47
+ const isMany = relation.collection === options.parentCollection;
48
+ return isMany ? relation.field : relation.meta?.one_field;
49
+ })
50
+ : allowedFields.filter((fieldKey) => !!getRelation(context.schema, options.parentCollection, fieldKey));
51
+ const nonRelationalFields = allowedFields.filter((fieldKey) => relationalFields.includes(fieldKey) === false);
52
+ const aliasFields = Object.keys(options.query.alias ?? {}).map((fieldKey) => {
53
+ const name = options.query.alias[fieldKey];
54
+ if (relationalFields.includes(name)) {
55
+ return `${fieldKey}.${parts.slice(1).join('.')}`;
56
+ }
57
+ return fieldKey;
58
+ });
59
+ fields.splice(index, 1, ...[
60
+ ...relationalFields.map((relationalField) => {
61
+ return `${relationalField}.${parts.slice(1).join('.')}`;
62
+ }),
63
+ ...nonRelationalFields,
64
+ ...aliasFields,
65
+ ]);
66
+ }
67
+ }
68
+ return fields;
69
+ }
@@ -0,0 +1,15 @@
1
+ import type { Accountability, Query, SchemaOverview } from '@directus/types';
2
+ import type { Knex } from 'knex';
3
+ import type { FieldNode, FunctionFieldNode, NestedCollectionNode } from '../../../types/index.js';
4
+ export interface ParseFieldsOptions {
5
+ accountability: Accountability | null;
6
+ parentCollection: string;
7
+ fields: string[] | null;
8
+ query: Query;
9
+ deep?: Record<string, any>;
10
+ }
11
+ export interface ParseFieldsContext {
12
+ schema: SchemaOverview;
13
+ knex: Knex;
14
+ }
15
+ export declare function parseFields(options: ParseFieldsOptions, context: ParseFieldsContext): Promise<(NestedCollectionNode | FieldNode | FunctionFieldNode)[]>;